When changing the parent's state from a child, why does the parent not render again?

Multi tool use
Multi tool use


When changing the parent's state from a child, why does the parent not render again?



I have a parent class that passes a function called editClassInfo to a child class. This function, is bound to the parent, and when called, immutably changes the state of the parent. However, the parent does not render again, instead having to set the state from within the parent.


editClassInfo



Specifically, the problem is with a modal component that has a textInput that, when the text is changed, sets the state of the parent screen. However, when the modal is closed, the screen's state does not update immediately, even though the state is changed. Instead, another this.setState() has to be called in order for the render to happen again.


textInput


this.setState()



Here is a picture reference of the problem:
https://imgur.com/a/oCHRTIu



Here is the code:



This is the parent component.


export default class Classes extends Component {
static navigationOptions = {
title: 'Classes'
};

constructor() {
super();

this.state = {
numObjects: 3.,
classes: [
{
className: "Math",
teacherName: "Someone",
},
{
className: "Science",
teacherName: "Someone",
},
{
className: "Art",
teacherName: "Someone",
}
]
};

this.editClassInfo = this.editClassInfo.bind(this);
}

editClassInfo(index, infoType, value) {
let newClass = this.state.classes;
switch (infoType) {
case 'className':
newClass[index].className = value;
break;
case 'teacherName':
newClass[index].teacherName = value;
break;
}
this.setState({classes: newClass});
}

addClass(name, name2) {
let newClass = this.state.classes.concat({className: name, teacherName: name2});
this.setState({classes: newClass});
}

loadClasses = () => {
this.setState({
numObjects: this.state.numObjects * 2,
})
}

render(){
const classData = this.state.classes;
console.log(this.state.classes[0]);
return (
<View style={styles.container}>
<TopBar title='Classes'/>
<View style={styles.classHeader}>
<Text style={styles.currentClasses}> CURRENT CLASSES </Text>
<TouchableOpacity onPress={() => {this.addClass('World History', 'Someone')}}>
<Image
style = {styles.addClass}
source={require('../resources/addClass.png')}
/>
</TouchableOpacity>
</View>
<FlatList
data = { classData }
onEndReached = {this.loadClasses}
keyExtractor = {(item, index) => index.toString()}
initialNumtoRender = {3}
renderItem = {({item}) =>
<ClassBox
index={this.state.classes.indexOf(item)}
editClassInfo ={this.editClassInfo}
className={item.className}
teacherName={item.teacherName}
/>
}
/>
</View>
);
}
}



I pass editClassInfo onto a component called ClassBox:


editClassInfo


ClassBox


export default class ClassBox extends Component {
static propTypes = {
className: PropTypes.string.isRequired,
teacherName: PropTypes.string.isRequired,
index: PropTypes.number.isRequired
};

constructor(props) {
super(props);

this.state = {
isVisible: false,
};

this.modalVisible = this.modalVisible.bind(this);
}

modalVisible(visible) {
this.setState({isVisible: visible});
}

render(){
return(
<View>
<ClassEdit
index={this.props.index}
editClassInfo={this.props.editClassInfo}
isVisible={this.state.isVisible}
modalClose={this.modalVisible}
className={this.props.className}
teacherName={this.props.teacherName}
/>
<TouchableOpacity onPress={() => {this.modalVisible(true)}}>
<View style={styles.container}>
<Image
source={{uri: 'http://via.placeholder.com/50x50'}}
style={styles.classImage}
/>
<View style={styles.classInfo}>
<Text style={styles.className}>
{this.props.className}
</Text>
<Text style={styles.teacherName}>
{this.props.teacherName}
</Text>
</View>
</View>
</TouchableOpacity>
</View>
)
}
}



This component contains the Child Modal ClassEdit:


ClassEdit


export default class ClassEdit extends Component {
static propTypes = {
index: PropTypes.number.isRequired,
isVisible: PropTypes.bool.isRequired,
className: PropTypes.string.isRequired,
teacherName: PropTypes.string.isRequired
}

render() {
return(
<Modal
animationType="none"
transparent={false}
visible={this.props.isVisible}
>
<View style={styles.container}>
<View style={styles.closeTop}>
<TouchableOpacity onPress={() => {
this.props.modalClose(false);
}}>
<Image
style={styles.closeIcon}
source={require('../resources/close.png')}
/>
</TouchableOpacity>
</View>
<View style={styles.classInfo}>
<Image
source={{uri: 'http://via.placeholder.com/150x150'}}
style={styles.classImage}
/>
<TextInput
style={styles.className}
placeholder='Class Name'
value={this.props.className}
onChangeText = {(className) => {this.props.editClassInfo(this.props.index, 'className', className)}}
/>
<TextInput
style={styles.teacherName}
placeholder='Teacher Name'
value={this.props.teacherName}
onChangeText = {(teacherName) => {this.props.editClassInfo(this.props.index, 'teacherName', teacherName)}}
/>
</View>
</View>
</Modal>
);
}
}



It is in this last component called ClassEdit where the parent state gets changed, but when the modal is closed, the updated state is not seen, instead having to call addClass to trigger it.


ClassEdit


addClass



I am a bit new to react-native so my code might not be the best, and the problem might be really simple, but any help would be appreciated.





I think you problem is related to stackoverflow.com/questions/29537299/….
– anhtu
Jul 3 at 3:55




1 Answer
1



let newClass = this.state.classes; creates a reference to the actual classes state, you're then mutating it.


let newClass = this.state.classes;


classes



To create a new array in an immutable way you can do this :



ES6 :


let newClass = [...this.state.classes];



ES5 :


let newClass = .concat(this.state.classes);





Thank you. This is one solution to my problem, and probably the better one as it turns out I made a mistake when I tried to immutably update my state array. I used let newClass = this.state.classes.slice(), but I think it accomplishes the same thing (correct me if I'm wrong). Another solution, however, which is the one I found out myself before I saw your answer was to provide an extraData prop to the FlatList component which was a state value for which a function "refreshed" whenever a render was needed. In hindsight, your solution is much more concise and more syntactically correct. :D
– Infinity
Jul 3 at 19:33


let newClass = this.state.classes.slice()


extraData






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

b8sRZg8e3fP9YbeAX6Ys0Xn4u
aPQDxQfbXFUe

Popular posts from this blog

PHP contact form sending but not receiving emails

Do graphics cards have individual ID by which single devices can be distinguished?

Create weekly swift ios local notifications