我花了一个多星期才努力让它发挥作用。
在下面的代码中,数据通过道具(Redux模式)进入并被渲染,实际上有评估,形式类型。当你填写表格并提交表格时,它会被提交,道具和状态发生变化。 (该表单将从州,道具中删除)但该表单仍在视图中显示。在代码中调用this.loadData();
没有用。但是PullDownToRefresh
可以工作并更新组件。 RefreshControl
也在调用this.loadData();
我做错了什么?有解决方案吗? Evaluation.js:
import React, {Component, PropTypes} from "react";
import {
ActivityIndicator,
Text,
View,
Image,
NetInfo,
Alert,
TouchableOpacity,
ScrollView,
TextInput,
Dimensions,
RefreshControl,
Platform
} from 'react-native';
import { styles, moment, GoogleAnalytics, KeyboardAwareScrollView, DeviceInfo, Loader, Accordion, I18n, CustomNavBar, DatePicker, FMPicker, CustomStarRating, Icon, CustomPicker, CustomQuestion, CustomDatePicker } from "../../common/components";
let { width, height } = Dimensions.get('window');
var index = 0;
export default class Evaluation extends Component {
static propTypes = {
user: PropTypes.string.isRequired,
users: PropTypes.object.isRequired,
evaluation: PropTypes.object.isRequired,
getEvaluation: PropTypes.func.isRequired,
submitEvaluation: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
date: moment().format('DD-MM-YYYY'),
isRefreshing: false,
isLoading: true,
dsource: []
};
this.list = {};
}
componentWillMount(){
this.loadData();
}
componentWillReceiveProps(nextProps){
this.processData();
}
componentDidMount(){
NetInfo.isConnected.fetch().then(isConnected => {
this.setState({
isConnected: isConnected
});
});
NetInfo.isConnected.addEventListener(
'change',
isConnected => {
this.setState({
isConnected: isConnected
});
}
);
}
randomNumber(){
return ++index;
}
loadData(){
this.props.getEvaluation(this.props.users);
}
processData(){
let {user, evaluation} = this.props;
let data = evaluation[user];
let dsource = [];
let list = {};
if(data){
this.setState({
isLoading: true
});
if(Object.keys(data).length > 0){
Object.keys(data).forEach((e)=>{
let currentEvaluation = data[e];
let fields = [];
list[currentEvaluation.evaluationId] = {};
if(currentEvaluation.hasOwnProperty('evaluationField')){
if(currentEvaluation.evaluationField.length > 0){
currentEvaluation.evaluationField.forEach((f)=>{
fields.push({
...f,
value: ''
});
list[currentEvaluation.evaluationId][f.field_name] = {
value: '',
required: f.required
};
});
}
} else {
}
dsource.push({
id: currentEvaluation.evaluationId,
title: currentEvaluation.evaluationTitle,
expire: currentEvaluation.evaluationExpire,
image: currentEvaluation.evaluationImage,
fields: fields
});
});
}
}
this.list = list;
this.setState({
dsource,
isLoading: false
});
setTimeout(()=>{
this.forceUpdate();
}, 1000);
}
async getObjectToPost(evaluationID){
let obj = this.list;
return this.list[evaluationID];
}
async changeValue(a,b,c,type){
let list = this.list;
if(type == 'date' || type == 'picker'){
list[a][b].value = c;
} else {
let oldValue = this.getValue(a,b);
if(oldValue != c){
list[a][b].value = c;
}
}
this.list = list;
}
getValue(id, name){
let list = this.list;
return list[id][name].value;
}
async evaluationSubmit(evalid){
const {user, users, evaluation, getEvaluation, submitEvaluation} = this.props;
let allRequiredEnetered = true;
let objToPost = {};
let answers = await this.getObjectToPost(evalid);
for(let key in answers){
objToPost[key]=answers[key].value;
if(answers[key].required == true && answers[key].value == ''){
allRequiredEnetered = false;
}
}
if(allRequiredEnetered){
objToPost = {
result: objToPost
};
let stringifiedObject = JSON.stringify(objToPost);
if(this.state.isConnected){
submitEvaluation(user, users, evalid, stringifiedObject, ()=>{
console.log('Running callback');
Alert.alert(
I18n.t("evaluation_submitted_title"),
I18n.t("evaluation_submitted_desc")
);
setTimeout(()=>{
this.loadData();
}, 1000);
});
} else {
//// Save evaluation to submit later.
Alert.alert(
I18n.t("offline_mode_title"),
I18n.t("evaluation_offline")
);
}
} else {
Alert.alert(
I18n.t("invalid_input_title"),
I18n.t("please_fill_in")
);
}
}
renderQuestions(EvaluationFields,TotalEvaluationsCount,EvaluationID){
let tmdata = [];
for(let n=0; n < TotalEvaluationsCount; n++){
if(n > 0){
tmdata.push(
<View key={this.randomNumber()} style={styles.separator}></View>
);
}
tmdata.push(
<Text key={this.randomNumber()} style={styles.questionTitle} >{EvaluationFields[n].label}{EvaluationFields[n].required > 0 ? ' *' : ''}{'\n'}</Text>
);
switch (EvaluationFields[n].type) {
case 'date':
let currentValue = this.getValue(EvaluationID, EvaluationFields[n].field_name);
let dateToShow = this.props.date;
if(currentValue.length != undefined && currentValue.length != ''){
dateToShow = currentValue;
}
tmdata.push(
<View style={styles.datepicker} key={this.randomNumber()}>
<CustomDatePicker
mode="date"
placeholder={I18n.t("select_date")}
format="DD-MM-YYYY"
minDate="01-01-2000"
maxDate="01-01-2099"
showIcon={false}
confirmBtnText={I18n.t("confirm_button")}
cancelBtnText={I18n.t("login_page_scan_cancel")}
onDateChange={(date) => {this.changeValue(EvaluationID, EvaluationFields[n].field_name, date, 'date');}}
required={EvaluationFields[n].required > 0 ? true : false}
/>
</View>
);
break;
case 'text':
tmdata.push(
<TextInput
key={this.randomNumber()}
style={[styles.textinput, Platform.OS == "android" ? { borderWidth: 0, height: 35 } : {}]}
onChangeText={(text) => {this.changeValue(EvaluationID, EvaluationFields[n].field_name, text, 'text');}}
maxLength = {Number(EvaluationFields[n].max_length)}
autoCorrect={false}
autoCapitalize={'none'}
clearButtonMode={'always'}
placeholder={I18n.t("evaluations_comment_field")}
/>
);
break;
case 'rate':
tmdata.push(
<View key={this.randomNumber()} style={styles.starrating}>
<CustomStarRating
maxStars={Number(EvaluationFields[n].stars)}
rating={Number(this.getValue(EvaluationID, EvaluationFields[n].field_name))}
selectedStar={(rating) => {this.changeValue(EvaluationID, EvaluationFields[n].field_name, rating, 'rating');}}
starSize={(width / (Number(EvaluationFields[n].stars))) > ( width / 10) ? ( width / 10) : (width / (Number(EvaluationFields[n].stars)))}
required={EvaluationFields[n].required > 0 ? true : false}
/>
</View>
);
break;
}
if(EvaluationFields[n].type == 'list'){
if(EvaluationFields[n].widget == 'note'){
tmdata.push(
<View key={this.randomNumber()}>
<CustomQuestion
evaluationId={EvaluationID}
fieldName={EvaluationFields[n].field_name}
allowedValues={EvaluationFields[n].allowed_values}
noteColors={EvaluationFields[n].note_colors}
onChange={(value)=>{ this.changeValue(EvaluationID, EvaluationFields[n].field_name, value, 'custom') }}
required={EvaluationFields[n].required > 0 ? true : false}
/>
</View>
);
} else {
let allowedValues = EvaluationFields[n].allowed_values;
let Options=[];
let LabelsForOptions=[];
for(let r=0; r < allowedValues.length; r++){
Options.push(allowedValues[r][0]);
LabelsForOptions.push(allowedValues[r][1]);
}
tmdata.push(
<View style={Platform.OS == "ios" ? styles.picker : styles.pickerSimple} key={this.randomNumber()}>
<CustomPicker
options={Options}
labels={LabelsForOptions}
onSubmit={(option) => {this.changeValue(EvaluationID, EvaluationFields[n].field_name, option, 'picker');}}
confirmBtnText={I18n.t("confirm_button")}
cancelBtnText={I18n.t("login_page_scan_cancel")}
text={I18n.t("please_select_answer")}
required={EvaluationFields[n].required > 0 ? true : false}
/>
</View>
);
}
}
}
return(
<View key={this.randomNumber()}>{tmdata}</View>
);
}
render() {
let dsource = this.state.dsource;
return (
<View style={styles.container}>
<View style={{ width: width, height: Platform.OS == "ios" ? 64 : 54}}>
<CustomNavBar
width={width}
height={Platform.OS == "ios" ? 64 : 54}
title={I18n.t("evaluation_page_nav_title")}
titleSize={18}
buttonSize={15}
background={"#00a2dd"}
color={"#FFF"}
rightIcon={"ios-person-outline"}
rightIconSize={30}
rightAction={()=> { this.props.openProfile(); }}
/>
</View>
<View style={{ height: Platform.OS == "ios" ? height - 114 : height - 130 }}>
{!this.state.isLoading ?<ScrollView
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
onRefresh={this.loadData.bind(this)}
tintColor="#00a2dd"
title=""
titleColor="#00a2dd"
colors={['#00a2dd', '#00a2dd', '#00a2dd']}
progressBackgroundColor="#FFFFFF"
/>
}
>
{dsource.length > 0 ?
<View style={styles.container}>
<View>
<KeyboardAwareScrollView>
<View>
{dsource.map((data)=>{
return(
<View style={styles.cardContainer} key={this.randomNumber()}>
<View style={styles.cardHeader} >
<View style={styles.headerImageContainer}>
<Image style={styles.headerImage} source={{uri: data.image}} />
</View>
<View style={{ margin: 5 }}>
<Text style={styles.cardTitle}>{data.title}</Text>
</View>
</View>
<View style={{ padding: 5 }}>
{this.renderQuestions(data.fields, data.fields.length, data.id)}
</View>
<View style={{ padding: 5 }}>
<View style={styles.separator}></View>
<Text style={styles.footerText}>{I18n.t("evaluations_mandatory")}{'\n'}{I18n.t("evaluations_desc_expire")} {data.expire}</Text>
<TouchableOpacity onPress={() => this.evaluationSubmit(data.id)} style={styles.submitButton} >
<Text style={styles.buttonText}>{I18n.t("evaluations_submit_button")}</Text>
</TouchableOpacity>
</View>
</View>
);
})}
</View>
</KeyboardAwareScrollView>
</View>
</View>
:
<View style={styles.errorContainer}>
<View style={styles.error}>
<Text style={styles.Errortext}>
{I18n.t("evaluations_no_evaluation_available")}
</Text>
</View>
</View>
}
</ScrollView>
:<ActivityIndicator
animating={true}
style={{ paddingTop: Platform.OS == "ios" ? (height - 114)/2 : (height - 130)/2 }}
color={'#00a2dd'}
size={'small'}
/>}
</View>
</View>
);
}
}
EvaluationPage.js
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import Evaluation from "./components/Evaluation";
import {Actions as routes} from "react-native-router-flux";
import * as evaluationActions from "./evaluation.actions";
function mapStateToProps(state) {
return {
user: state.auth.user,
users: state.auth.users,
evaluation: state.evaluation.evaluation,
openProfile: routes.profilePage
}
}
function dispatchToProps(dispatch) {
return bindActionCreators({
getEvaluation: evaluationActions.getEvaluation,
submitEvaluation: evaluationActions.submitEvaluation
}, dispatch);
}
export default connect(mapStateToProps, dispatchToProps)(Evaluation);
Evaluation.actions.js:
export const GET_EVALUATION = 'GET_EVALUATION';
export const GET_EVALUATION_FAILED = 'GET_EVALUATION_FAILED';
export const SUBMIT_EVALUATION = 'SUBMIT_EVALUATION';
export const SUBMIT_EVALUATION_FAILED = 'SUBMIT_EVALUATION_FAILED';
import Functions from '../common/Functions';
export const getEvaluation = (users) => {
return dispatch => {
Functions.getEvaluationsAPI(users)
.then((data)=>{
const {evaluationsList} = data;
return dispatch(evaluationSuccess(evaluationsList));
})
.catch((e)=>{
return dispatch(evaluationFailed(e));
});
};
}
export const submitEvaluation = (user, users, evaluationId, evaluationData, callback) => {
return dispatch => {
Functions.submitEvaluation(user, users, evaluationId, evaluationData)
.then(()=>{
console.log('Submit finished, getting evaluations');
Functions.getEvaluationsAPI(users)
.then((data)=>{
const {evaluationsList} = data;
console.log('Got evals list', evaluationsList);
return dispatch(evaluationSubmissionSuccess(evaluationsList));
})
.catch((e)=>{
return dispatch(evaluationSubmissionFailed(e));
});
})
.catch((e)=>{
return dispatch(evaluationSubmissionFailed(e));
});
};
}
const evaluationSuccess = (evaluationsList) => {
return {
type: GET_EVALUATION,
payload: {
evaluation: evaluationsList
}
}
};
const evaluationFailed = (e) => {
return {
type: GET_EVALUATION_FAILED,
payload: {
error: e
}
}
};
const evaluationSubmissionSuccess = (evaluationsList) => {
console.log('Submission success returning to reducer');
return {
type: SUBMIT_EVALUATION,
payload: {
evaluation: evaluationsList
}
};
};
const evaluationSubmissionFailed = (e) => {
return {
type: SUBMIT_EVALUATION_FAILED,
payload: {
error: e
}
};
};
Evaluation.reducer.js:
import * as types from "./evaluation.actions";
export const INITIAL_STATE = {
evaluation: {}
};
export default function evaluation(state = INITIAL_STATE, action){
const {evaluation} = state;
switch(action.type){
case types.GET_EVALUATION:
return Object.assign({}, state, {
evaluation: action.payload.evaluation
});
case types.GET_EVALUATION_FAILED:
return {
...state,
evaluation
};
case types.SUBMIT_EVALUATION:
let newObject = Object.assign({}, state, {
evaluation: action.payload.evaluation
});
return newObject;
case types.SUBMIT_EVALUATION_FAILED:
return {
...state,
evaluation
};
default:
return state;
}
}
Store.js
import { persistStore, autoRehydrate } from "redux-persist";
import { combineReducers } from "redux";
import {REHYDRATE} from 'redux-persist/constants';
import { applyMiddleware, createStore, compose } from "redux";
import createActionBuffer from 'redux-action-buffer';
import { AsyncStorage } from "react-native";
import thunk from "redux-thunk";
import createLogger from "redux-logger";
import rootReducer from "./rootReducer";
const logger = createLogger();
const enhancer = compose(
autoRehydrate(),
applyMiddleware(
thunk,
logger,
createActionBuffer(REHYDRATE)
)
);
const store = createStore(
rootReducer,
{},
enhancer
);
persistStore(store, {storage: AsyncStorage});
export default store;
rootReducer.js
import { combineReducers } from "redux";
import auth from "./auth/auth.reducer";
import storage from "./storage/storage.reducer";
import courses from "./courses/courses.reducer";
import goals from "./goals/goals.reducer";
import evaluation from "./evaluation/evaluation.reducer";
const rootReducer = combineReducers({
auth,
storage,
courses,
goals,
evaluation
});
export default rootReducer;
Root.js:
import React from "react";
import { View } from "react-native";
import { Provider } from "react-redux";
import store from "./store";
import Routes from "./Routes";
const Root = () => (
<Provider store={store}>
<View style={{flex: 1}}>
<Routes />
</View>
</Provider>
);
export default Root;
答案 0 :(得分:2)
刚刚发现了这个问题。在componentWillReceiveProps
我正在调用this.processData()
,而processData()
正在从this.props
获取数据,我应该已将nextProps
传递给processData()
并且从nextProps
读取数据。
componentWillReceiveProps(nextProps){
this.processData(nextProps);
}
processData(nextProps){
/// process data based on nextProps
}
答案 1 :(得分:1)
您需要使用connect。将Component连接到redux商店。
添加导入:
import {connect} from 'react-redux';
将班级声明更改为:
class Evaluation extends Component {...
然后在底部添加连接导出:
export default connect()(Evaluation);