Ben
Ben

Reputation: 27

When storing a component in state, it no longer re-renders when its props are changed?

In a particular business case, I have a series of components that are laid out in a linear fashion, however the order of which may change. For example, we have a parent component:

export const Component = () => {

    const [data, setData] = useState(null);

    const [nextComponent, setNextComponent] = useState(null);

    return (
        //... component code here
        {nextComponent}
    );
}

Inside the Component code would be buttons that can change which component is to come next:

onClick={() => setNextComponent(<SomeComponent data={data} setData={setData} />)}

If data changes by calling setData within SomeComponent, the component is not re-rendering with the new data prop value.

I understand this is something to do with the SomeComponent being stored in state, but can not understand why data does not update in SomeComponent - surely data is just a pointer to a memory location, and should therefore have the same value across the parent and child?

Upvotes: 0

Views: 57

Answers (1)

Chris Cashwell
Chris Cashwell

Reputation: 22899

This is because the data prop is only updated for SomeComponent when the function setNextComponent is called as part of your onClick callback.

The "right way" to do this is to hold the data that matters to SomeComponent in its parent's state, then pass it down as part of a conditional render of that child.

const ChildComponent = ({ data, setData }) => (
  <React.Fragment>
    <span>Current Data: {JSON.stringify(data)}</span>
    <input onChange={({target}) => setData(target.value)} placeholder="Enter New Data" />
  </React.Fragment>
);

const ParentComponent = () => {
  const CHILD_TYPES = { signIn: SomeComponent, signOut: OtherComponent };

  // keep track which component you want to be showing based on parent state
  const [activeComponentType, setActiveComponentType] = useState('signIn');

  // keep track of the data required for the child component(s)
  const [data, setData] = useState(null);

  // find and use the correct class for the current child type
  // (you could do this without an extra variable by using `React.createClass`)
  const ActiveComponentClass = CHILD_TYPES[activeComponentType];
  return (<ActiveComponentClass data={data} setData={setData} />);
}

Also, you should consider not storing components in state. It's bad practice and usually won't work the way you expect.

Upvotes: 1

Related Questions