Reputation: 3831
Let's say we have a component called MyChildComponent which would be used for displaying a number list and also the ability to add a random number into the list via button click inside MyChildComponent. When button clicks, only MyChildComponent get re-render because only the state of MyChildComponent gets updated.
public class MyParentComponent extends React.Component {
const numsInParent = [0,1,2];
render() {
return <MyChildComponent nums="numsInParent " />);
}
}
public class MyChildComponent extends React.Component {
const { nums } = this.props;
state = { nums: this.props.nums };
handleChange = ()=> {
this.setState({ nums: {...nums, random()} });
};
render() {
return (
<button onClick="this.handleChange">Add<button>
<ul>
nums.map((num) =><li>{num}</li>)
</ul>);
}
}
However, oftentimes I was told we should not do as above design. We should have the parent component passing the nums
and also a function into child component props
, instead of implementing the actual method directly inside the child component, it would be done in parent component and value should be updated through child component props, which is so-called controlled component, like
public class MyParentComponent extends React.Component {
addNum() {
numsInParent = {...numsInParent, random()};
}
render() {
return <MyChildComponent nums="numsInParent " addNum="this.addNum" />);
}
}
public class MyChildComponent extends React.Component {
const { nums, addNum } = this.props;
handleChange = ()=> {
// child just call parent to do the real work
this.addNum();
};
render() {
return (
<button onClick="this.handleChange">Add<button>
<ul>
nums.map((num) =><li>{num}</li>)
</ul>);
}
}
Doing this way, when button click inside the child component, it would cause both parent and child component to get re-render in a way of child -> parent -> child, correct? So what would be the benefit of this design instead of the first one, would it cause any performance impact? I've seen example project that has a structure as
ComponentA(actual method) -> ComponentB -> ComponentC -> ... ComponentX
,
In order to figure out how something getting changed in ComponentX, we need to go all the way up to where the actual method being declared in ComponentA, does it also mean when something changed triggered in ComponentX, all Component from A, B, C to X will get a re-render? Does it worth that?
Upvotes: 4
Views: 980
Reputation: 562
The statement about having the state in the parent makes it a controlled component is not what controlled and uncontrolled components are about. A controlled component is a component that lets React handle form data for it internally whereas an uncontrolled component is one that stores the form data in the DOM. So, since you are storing the data for everything in React's internal state, you are always creating a controlled component.
However, to answer the question about where to store that state, storing the state in the parent is only useful when the parent needs to access the information. Based on React documentation and React guides, always use the minimal amount of state as possible, and always push the state as low as it needs to go.
Looking at your code, the reason someone told you that what you were doing was wrong was because you were passing down a prop from the parent and then using that prop to populate the state. This can create a pointer to the array and any values you change in the child, will update the props of your parent, which will cause sync issues. Since your parent doesn't need to know about the state, no need to pass down the initial values to the child. To fix your code, it should look more like this:
public class MyParentComponent extends React.Component {
render() {
return <MyChildComponent />);
}
}
public class MyChildComponent extends React.Component {
state = { nums: [0,1,2]};
handleChange = ()=> {
this.setState({ nums: [...nums, random()] });
};
render() {
return (
<button onClick="this.handleChange">Add<button>
<ul>
nums.map((num) =><li>{num}</li>)
</ul>);
}
}
The only reason a parent would need to know about a state that it's child also needs to know about is if the parent needs to access the state as well as the child (some cases you may want to control parent functionality based on a state and the child also needs to change it's functionality based on the same state).
Otherwise, you are correct, and both the parent and child will do a re-render. And for the parent, that is a wasted render and does not need to be done, as nothing changed for the parent. Only the child would need to re-render.
This is a subject more about optimization, and having the state only in the child has better performance optimization then in the parent. Will you notice the difference with your eye, not likely. But having the state at the correct level can optimize your components and make them more efficient.
Upvotes: 3