2

I have parent component, which contains a global value in its state. Change of this value must trigger change of states of children components as in following example:

function Parent(props) {
const [parentState, setParentState] = useState(someState);

return (
  <button onClick={() => setParentState(newState)>Button</button>
  <ChildComponent parentState={parentState} />
}

function Child(props) {
const [childState, setChildState] = useState(getState(props.parentState));
}

Click on the button in this example should change trigger non-trivial change of the state in the Child component (which uses parentState as a parameter to generate its own state).

I guess the first answer to the problem would be to lift the state up - however I would like not to do that, because there are many Child generated through a .map function - I would need to retain their states in an array and propagate it to all the child components. The value in the parent component is a global value relevant for all the children and all the children must change their states when the parentState changes.

Is there any good way to achieve that without lifting the Child components' states? I hope I have described the problem well enough - if not I will be glad to update the information.

Faire
  • 706
  • 1
  • 9
  • 31
  • 1
    Does this answer your question? [React js change child component's state from parent component](https://stackoverflow.com/questions/39041710/react-js-change-child-components-state-from-parent-component) – Aurélien Foucault Feb 21 '20 at 14:39
  • Can you elaborate a bit on how the parent's global value is intended to be applied to the children? Do you apply the entire `parentState` to the children? If so, do the children even need their own states? I think your question implies that the parent sort of 'resets' the children on demand, but the children can then deviate and differ in state from the parent until the next 'reset' from the parent snaps the children back to being identical to the parent. Am I on the right track? – WGriffing Feb 21 '20 at 15:18
  • Aurélien: I am already transferring parent's state to the children, problem is that in the children the state is set on initialization (see Child state) and not change on subsequent changes of parent's state. @WGriffing: The child loads an array of data dependent on parent's state (say user's global settings) and it's own, non-derivative state (component's setting). These two factors are passed as parameters of function that returns the data for the child. – Faire Feb 24 '20 at 07:19

3 Answers3

4

Based on your clarifying comment, here's some ideas on how to proceed. Depending on what a parentState object contains, there might better ways to do this, but hopefully it will help spur some ideas.

  1. Use parentState in 2 ways.
    1. As a Child prop. This will always be updated whenever the Parent changes its value.
    2. As the initial value of one of the Child's useState hooks. This will initialize the child's derivative state, but will allow the Child state to deviate over time based on user actions within the Child component.
  2. Use useEffect hook(s) to trigger your Child getData function when the parentState variable changes or when the childStateSettings variable changes.
  3. Use multiple useState hooks in Child to separate the non-derivative part of the state from the inherited/derivative part of the Child's state. For the derivative state, childStateSettings, use parentState as the initial value.

So the Child becomes something like:

import { useEffect, useState } from 'react';

function Child(props) {
  const { parentState } = props;

  // initial value of child settings from parent
  const [childStateSettings, setChildStateSettings] = useState(parentState); 

  // non-inherited/non-derivative state of child
  const [childStateData, setChildStateData] = useState({}); 

  // Depending on the number of settings and ways in which
  //  the user can make them different than the parent/global settings,
  //  you may want to have even more useState hooks to further separate
  //  the settings into individual pieces. I think this would simplify
  //  your various `onChange`/`onSelection'/etc. function logic, but I
  //  couldn't say for certain without more details.


  useEffect(() => {
    getData(childStateDerivative, childStateNonDerivative);
  }, [parentState, childStateSettings]};

  // pseudocode invented by me as a guess of 
  //  how you might need to use things based on your comment
  const getData = (parentState, childState) => {
    // last spread object takes precedence if duplicate keys are present in both objects
    const combinedState = { ...parentState, ...childState }; 
    API.get('http://some_path', combinedState).then(data => {
      setChildStateData(data);
    });
  };

  // you'll probably draw some inputs so that the 
  //  user can alter the child's state settings but
  //  I didn't bother trying to guess what that might look like.
  return (<>...</>); 

}
WGriffing
  • 604
  • 6
  • 12
1

Child should not have a state of of it's own. It should completely depend on parent state/props.

function Parent(props) {
const [parentState, setParentState] = useState(someState);

return (
  <button onClick={() => setParentState(newState)>Button</button>
  <ChildComponent parentState={parentState} />
}

function Child(props) {
}
kooskoos
  • 4,622
  • 1
  • 12
  • 29
0

When you change the parent state the child will redraw. You can use setState() in the constructor to modify your child state while it is being recreated in the DOM.

Geoff
  • 353
  • 3
  • 19
  • Perhaps I am missing something but the child's data are not changed. I am using useState, it sets the data on initialization, but I do not see any way to trigger it afterwards. The child component does not change it's state after parent's state is changed, even though I am passing the state to the child. – Faire Feb 24 '20 at 07:21
  • Is the state supposed to change on the button click? The useState has 2 members: The state variable and the callback hook to change that variable. Beyond that we would need to see the code for the child component also. Without full code of the 2 components there isn't much more I can tell you. – Geoff Feb 24 '20 at 18:53