3

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?

Drex
  • 3,346
  • 9
  • 33
  • 58
  • 1
    "ComponentA, does it also mean when something changed triggered in ComponentX, all Component from A, B, C to X will get a re-render?" -> Yes, unless if you memoize it, or use `React.memo`. This is a question where everyone has their own opinion, much like global vs local state. Personally, I ask the question "purely semantically, where does this information best belong?" and write my abstractions accordingly. – Slava Knyazev Feb 12 '21 at 02:47
  • ```Personally, I ask the question "purely semantically, where does this information best belong?" and write my abstractions accordingly.``` This a great suggestion! I always do the same. If your parent doesn't need to know about the information, then why provide it to it. Separation of concerns. – Jason Bellomy Feb 12 '21 at 02:53
  • Does this answer your question? [Controlled vs uncontrolled components in React](https://stackoverflow.com/questions/44471370/controlled-vs-uncontrolled-components-in-react) – Heretic Monkey Feb 12 '21 at 03:44

1 Answers1

2

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.

Jason Bellomy
  • 442
  • 2
  • 7
  • Thanks a lot! Good to know that `This is a subject more about optimization`, I was thinking this is some kind of design pattern in React that must follow so that I was kinda confused, as I got questioned from other reviewers that this is not a good way to do. Even though in my work, my parent component doesn't need to care about what or how my child component change the data, all its job is to fetch the data and pass it along to the child component, but I was asked to implement the data update logic in parent instead in child – Drex Feb 12 '21 at 02:56
  • Right, so then your parent needs to know about the data so that it can update it and call to get the new results in the load. Therefore, you would want to move your state up to the parent. In that case, that is a good example of where the parent would need to know about the data. – Jason Bellomy Feb 12 '21 at 03:21