简体   繁体   中英

React update parent when children component updated

Why parent component always show 1 less than child? It's not re-rendered after first update.

export const Parent = (props) => {
  const [count, setCount] = React.useState(0);
  return (
    <View>
      <Text>Parent: {count}</Text>
      <Child updateCount={setCount} />
    </View>
  );
};

const Child = (props) => {
  const [count, setCount] = React.useState(0);

  const updateCount = () => {
    setCount((count) => count + 1);
    return props.updateCount(count);
  };
  return (
    <View>
      <Text>Child: {count}</Text>
      <Button title="add" onPress={updateCount} />
    </View>
  );
};

The main issue is you are expecting setCount to be happening immediately although it is an asynchronous action. The problem is in your <Child /> component in the below code:

const updateCount = () => {
    setCount((count) => count + 1)
    return props.updateCount(count)
}

Technically you are expecting count to be updated and passed to props.updatedCount meanwhile it's still has the previous value.

Also which is not related to the issue but you don't really need to return from the function where you update the state, so just removed return keyword.


There are different options to solve this issue.

  1. Using still the separated states as in your code:

Simply calling exactly the same way the props.updateCount as the other setState as:

const updateCount = () => {
  setCount((count) => count + 1)
  props.updateCount((count) => count + 1)
}
  1. Keeping one state in <Parent /> and passing down through props:

So mainly the solution would be:

export const Parent = (props) => {
  const [count, setCount] = React.useState(0);
  return (
    <View>
      <Text>Parent: {count}</Text>
      {/* passed here count as well: */}
      <Child updateCount={setCount} count={count} />
    </View>
  );
};

// destructure here updateCount and count
const Child = ({ updateCount, count }) => {
  return (
    <View>
      {/* using here count from parent */}
      <Text>Child: {count}</Text>
      {/* calling here updateCount from the props as: */}
      <Button title="add" onPress={() => updateCount((count) => count + 1} />
    </View>
  );
};
  1. The third option would be to use Context API :

By using Context you can do the following, see from the documentation as:

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Usually I like to use Context once we have more complex component tree than the current one. So I suggest to go with keeping one state option.

The solution N°2 of @norbitrial worked as a charm, in my case it is class components not functional components. here only to post an answer adapted to class components So the idea is keeping on state in parent component and share it with the child as props. then make any operation we want on it in child component.

Parent.js

export default class CraGenerator extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            number: 0
        }

    getNumberFromChild(data) {
            this.setState({ number: data });
    }

    render() {
        return (
            <div onClick={this.increment.bind(this)}>
                <Child getNumberFromChild={this.getNumberFromChild.bind(this)} number={this.state.number}/>
            </div>
            
        )
}

Child.js

export default class Child extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            number: this.props.number
        }
    }

    increment() {
        this.setState({ number: this.state.number + 1 });
        this.props.getNumberFromChild(this.state.number + 1);
    }

    render() {
        return (
            <div onClick={this.increment.bind(this)}>
                Click me...increment me ::: {this.state.number}
            </div>
        );
    }
}

Move the logic part of your code to the parent and leave the child as a button and text only instead. Do it like this:

 export const Parent = (props) => {
  const [count, setCount] = React.useState(0);

  const handleCount = () => {
    setCount = count + 1
  }

  return (
    <View>
      <Text>Parent: {count}</Text>
      <Child updateCount={setCount} count={count}/>
    </View>
  );
};

const Child = (props) => {

  return (
    <View>
      <Text>Child: {props.count}</Text>
      <Button title="add" onPress={()=>props.updateCount()} />
    </View>
  );
};

I made an example here :

const updateCount = () => {
  setCount((count) => count + 1); // <=== you did it here correctly
  return props.updateCount(count); // the problem is here
};

This is happening because it's not receiving the latest value of count, which that happens on the next re-render, so it will always be one step behind. To fix this just do the same, return here is redundant.

const updateCount = () => {
  setCount((count) => count + 1);
  props.updateCount((count) => count + 1); // problem fixed
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM