行动结束后,道具正在改变。 componentWillUpdate也会触发,但组件不会重新渲染。
参见代码:
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');
GoogleAnalytics.setTrackerId('UA-86421142-1');
GoogleAnalytics.trackScreenView('Evaluation Page');
GoogleAnalytics.setDispatchInterval(5);
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 = {
isLoading: true,
evaluationList: '',
totalEval: 0,
date: moment().format('DD-MM-YYYY'),
isRefreshing: false
};
this.list = {};
}
componentDidMount(){
this.loadData();
NetInfo.isConnected.fetch().then(isConnected => {
this.setState({
isConnected: isConnected
});
});
NetInfo.isConnected.addEventListener(
'change',
isConnected => {
this.setState({
isConnected: isConnected
});
}
);
}
shouldComponentUpdate(nextProps, nextState){
let shouldUpdate = false;
const oldValue = this.props.evaluation[this.props.user];
const newValue = nextProps.evaluation[nextProps.user];
Object.keys(newValue).forEach((index)=>{
if(!oldValue.hasOwnProperty(index)){
shouldUpdate = true;
}
});
Object.keys(oldValue).forEach((index)=>{
if(!newValue.hasOwnProperty(index)){
shouldUpdate = true;
}
});
console.log('should component update?', shouldUpdate);
return shouldUpdate;
}
randomNumber(){
index++;
return index;
}
async loadData(cb = ()=>{}){
await this.props.getEvaluation(this.props.users);
let {user, users, evaluation, getEvaluation} = this.props;
let data = evaluation[user];
let dsource = [];
let list = {};
Object.keys(data).forEach((e)=>{
let currentEvaluation = data[e];
let fields = [];
list[currentEvaluation.evaluationId] = {};
currentEvaluation.evaluationField.forEach((f)=>{
fields.push({
...f,
value: ''
});
list[currentEvaluation.evaluationId][f.field_name] = {
value: '',
required: f.required
};
});
dsource.push({
id: currentEvaluation.evaluationId,
title: currentEvaluation.evaluationTitle,
expire: currentEvaluation.evaluationExpire,
image: currentEvaluation.evaluationImage,
count: currentEvaluation.evaluationField.length,
fields: fields
});
});
this.list = list;
this.setState({
evaluationList: dsource,
isLoading: false,
totalEval: dsource.length,
});
this.forceUpdate();
cb();
}
async getObjectToPost(evaluationID){
let obj = this.list;
return obj[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;
}
async getValue(id, name){
let list = this.list;
return list[id][name].value;
}
async startEvaluationSubmission(user, users, id, data, cb){
await cb(user, users, id, data, ()=>{
Alert.alert(
I18n.t("evaluation_submitted_title"),
I18n.t("evaluation_submitted_desc")
);
});
}
async submitEvaluation(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){
this.startEvaluationSubmission(user, users, evalid, stringifiedObject, submitEvaluation);
} 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){ // evald.fields, evald.count, evald.id
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>
);
}
renderRow() {
if(!this.state.isLoading){
let eval_length = this.state.totalEval;
let content = [];
let evaluationList = this.state.evaluationList;
for(let x=0; x < eval_length; x++){
let evald = evaluationList[x];
content.push(
<View style={[styles.cardContainer, (x+1) == eval_length ? { marginBottom: 6 } : {}]} key={this.randomNumber()}>
<View style={styles.cardHeader} >
<View style={styles.headerImageContainer}>
<Image style={styles.headerImage} source={{uri: evald.image}} />
</View>
<View style={{ margin: 5 }}>
<Text style={styles.cardTitle}>{evald.title}</Text>
</View>
</View>
<View style={{ padding: 5 }}>
{this.renderQuestions(evald.fields, evald.count, evald.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")} {evald.expire}</Text>
<TouchableOpacity onPress={() => this.submitEvaluation(evald.id)} style={styles.submitButton} >
<Text style={styles.buttonText}>{I18n.t("evaluations_submit_button")}</Text>
</TouchableOpacity>
</View>
</View>
);
}
return(
<View>
<KeyboardAwareScrollView>
<View key={this.randomNumber()}>
{content}
</View>
</KeyboardAwareScrollView>
</View>
);
}
}
renderData(){
if(this.state.totalEval > 0){
return(
<View style={styles.container} key={this.randomNumber()}>
{this.renderRow()}
</View>
);
} else {
return(
<View style={styles.errorContainer}>
<View style={styles.error}>
<Text style={styles.Errortext}>
{I18n.t("evaluations_no_evaluation_available")}
</Text>
</View>
</View>
);
}
}
render() {
const {user, users, evaluation, getEvaluation} = this.props;
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"
/>
}
>
{this.renderData()}
</ScrollView>
:<ActivityIndicator
animating={true}
style={{ paddingTop: Platform.OS == "ios" ? (height - 114)/2 : (height - 130)/2 }}
color={'#00a2dd'}
size={'small'}
/>}
</View>
</View>
);
}
}
控制台日志输出:
有任何解决方案吗?
更新: 整个代码已添加到问题中。
评估提交后,道具发生变化。提交的评估将从评估列表中删除,但仍将呈现。通过RefreshControl调用loadData()(刷新下拉)将正确重新渲染,评估将被删除。
提前致谢。
答案 0 :(得分:1)
我遇到了类似的问题,其中道具不会流下来列出我从父帐户呈现的项目。所以我看了你的代码。
目前您遇到的情况如下:
this.loadData();
componentDidMount
shouldComponentUpdate
将返回true。< / LI>
shouldComponentUpdate
返回true
,则会触发componentWillUpdate
,最终会render
componentWillUpdate()
,componentDidMount
只会被调用一次。因此,您的组件无法获取新数据,因此render
将无法显示新数据。
如果不真正了解您的应用,我会尝试添加一个调用componentWillUpdate
的{{1}}函数,该函数会刷新您的状态并触发最后一次使用this.loadData
的重新渲染forceUpdate
函数的行。
也许检查https://facebook.github.io/react/docs/react-component.html以查看在组件生命周期中触发了哪些功能。
但是,为了使您的应用程序更易于维护并且可能更快,您可能需要考虑将数据提取和渲染拆分为不同的组件。
由于您已经在使用redux,因此最好在redux中处理所有数据提取。所以你有以下内容:
loadData
和getEvaluation()
等函数将评估加载/保存到服务器... startEvaluationSubmission()
state.evaluations
getEvaluation(userID)
。EvaluationsList.jsx
这样,如果商店中的任何数据发生更改,容器将自动触发容器更新并向下显示到显示组件。您可能不需要覆盖react的'shouldComponentUpdate`函数,因为它非常好并且非常有效。
Abhi Aiyer在redux上写了一些非常好的文章:https://medium.com/front-end-developers/how-we-redux-part-1-introduction-18a24c3b7efe#.gr289pzbi
尤其是第5部分可能对您有意义: https://medium.com/modern-user-interfaces/how-we-redux-part-5-components-bddd737022e1#.izwodhwwk
希望有所帮助......这就是我在不玩代码的情况下所能看到的。让我知道你是怎么过的。