[英]React Native: Props changed, component didn't re-render
After an action, props are being changed.在一个动作之后,道具正在被改变。 componentWillUpdate also triggers but, component is not re-rendering.
componentWillUpdate 也会触发,但组件不会重新渲染。
See the code :看代码:
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>
);
}
}
Console log output :控制台日志输出:
Any solutions please?请问有什么解决办法吗?
UPDATE : Whole code have been added to the question.更新:整个代码已添加到问题中。
After evaluation submission, props changes.提交评估后,道具发生变化。 the submitted evaluation will be removed from the evaluation list, but it will be still rendered.
提交的评估将从评估列表中删除,但仍会呈现。 calling the loadData() through RefreshControl (Pulldown to refresh) will re-render correctly and the evaluation will be removed.
通过 RefreshControl (Pulldown to refresh) 调用 loadData() 将正确重新渲染并且评估将被删除。
Thanks in advance.提前致谢。
I ran into a similar problem, where props wouldn't trickle down to list items I rendered form a parent account.我遇到了类似的问题,道具不会细细地列出我从父帐户呈现的项目。 So I had a look at your code.
所以我看了你的代码。
What currently happens for you is the following:您目前发生的情况如下:
this.loadData();
this.loadData();
in componentDidMount
componentDidMount
shouldComponentUpdate
returns true if any of the new user details didn't exist in the old user object or if any that did in the old user object exist are missing in the new one.shouldComponentUpdate
将返回 true。shouldComponentUpdate
returns true
, that will trigger componentWillUpdate
and eventually render
shouldComponentUpdate
返回true
,则将触发componentWillUpdate
并最终render
componentWillUpdate()
isn't defined in your code, and componentDidMount
only gets called once. componentWillUpdate()
未在您的代码中定义,并且componentDidMount
仅被调用一次。 So your component doesn't know fetch the new data and therefore render
won't have the new data to display.所以你的组件不知道获取新数据,因此
render
不会显示新数据。
Without really knowing more about your app, I'd try adding a componentWillUpdate
function that calls this.loadData
which will refresh your state and trigger a re-render with forceUpdate
on the last line of your loadData
function.在不太了解您的应用程序的情况下,我会尝试添加一个调用
this.loadData
的componentWillUpdate
函数,该函数将刷新您的状态并在您的loadData
函数的最后一行使用forceUpdate
触发重新渲染。
Maybe check https://facebook.github.io/react/docs/react-component.html to see which functions are triggered in the component lifecycle.也许检查https://facebook.github.io/react/docs/react-component.html以查看在组件生命周期中触发了哪些功能。
However to make your app easier to maintain and probably faster as well, you might want to consider splitting the data fetching and the rendering into different components.然而,为了使您的应用程序更易于维护并且可能更快,您可能需要考虑将数据获取和渲染拆分为不同的组件。
Since you're already using redux, all the data fetching should preferably be handled in redux.由于您已经在使用 redux,因此最好在 redux 中处理所有数据获取。 So you'd have something the following:
所以你会有以下内容:
getEvaluation()
and startEvaluationSubmission()
to load/save evaluations to the server...getEvaluation()
和startEvaluationSubmission()
等函数将评估加载/保存到服务器...state.evaluations
state.evaluations
getEvaluation(userID)
getEvaluation(userID)
等函数EvaluationsList.jsx
with just the evaluations it needs to render.EvaluationsList.jsx
。EvaluationContainer.jsx
EvaluationContainer.jsx
获取它自己的(并且只有那个)评估以显示为道具This way, if any of the data changes in the store, the container will automatically trigger an update of your container and trickle down to the display component.这样,如果存储中的任何数据发生更改,容器将自动触发容器的更新并向下传输到显示组件。 You probably won't need to override react's 'shouldComponentUpdate` function, as it's pretty good and really efficient.
您可能不需要覆盖 react 的 'shouldComponentUpdate' 函数,因为它非常好且非常有效。
Abhi Aiyer wrote up some really nice articles on redux: https://medium.com/front-end-developers/how-we-redux-part-1-introduction-18a24c3b7efe#.gr289pzbi Abhi Aiyer 写了一些关于 redux 的非常好的文章: https ://medium.com/front-end-developers/how-we-redux-part-1-introduction-18a24c3b7efe#.gr289pzbi
especially part 5 could be interesting for you: https://medium.com/modern-user-interfaces/how-we-redux-part-5-components-bddd737022e1#.izwodhwwk特别是第 5 部分可能对您很有趣: https : //medium.com/modern-user-interfaces/how-we-redux-part-5-components-bddd737022e1#.izwodhwwk
Hope that helps at all... that's all I could see without playing with the code.希望这有帮助......这就是我在不玩代码的情况下所能看到的。 Let me know how you get on.
让我知道你是怎么办的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.