Jessica
Jessica

Reputation: 9830

Why are all my component rerendering when I change an unrelated state?

I'm trying to change a state without having the entire app reload. For some reason that's not happening, and when I set the state of an object that isn't even passed down to a specific component, then that component updates for no reason.

So in the code below, I'm when I set the message, MainContent should not reload.

const [message, setMessage] = useState({});
setMessage('my message'); // This causes the entire app, including 'MyComponent' to reload

return (
    <div className='App'>
        <Header user={user} message={message} setMessage={setMessage} />

        <div id='main-site'>
            <Switch>
                // SHOULD NOT RELOAD UNLESS PASSED IN PROP CHANGED
                <Route path='/main' component={MainContent} />

            </Switch>
        </div>
    </div>
);

Header.jsx

return (
    {message && (
        <div>
            <span>{message.text}</span>
            // This on click makes the entire app reload
            <button className='close' onClick={() => setMessage({})}>
                X
            </button>
        </div>
    )}
);

How can I make sure the only components that have the message prop get updated, and no other components rerender?

Upvotes: 1

Views: 1957

Answers (2)

pery mimon
pery mimon

Reputation: 8317

You should define the hook into the component function. that binds the variable and it setMessage update function only to that component. In React the only way to update the dom is by recall all Pure Component or render function on React.Component and compare there return to previous return.

So to update the view after you change it by SetMessage react call again all render function to update the view.

function MyComponet(){
  return (
    <div className='App'>
        <Header user={user} message={'my message'} />

        <div id='main-site'>
            <Switch>
                // SHOULD NOT RELOAD UNLESS PASSED IN PROP CHANGED
                <Route path='/main' component={MainContent} />

            </Switch>
        </div>
    </div>
);
}


//state that updating when props updating too
function usePropsState(initialState) {
  const [state, setState] = useState(initialState);
  useEffect(() => setState(initialState), [initialState]);
  return [state, setState]
}

function Header({user, message:initMessage}){
  // now you can update the state internally or from outside
  const [message, setMessage] = usePropsState(initMessage);
  return (
    {message && (
        <div>
            <span>{message}</span>
            // This on click makes the entire app reload
            <button className='close' onClick={() => setMessage('')}>
                X
            </button>
        </div>
    )}
  );
}

If you like that custom hook thumb Up this issue

Upvotes: 0

Shubham Khatri
Shubham Khatri

Reputation: 281626

There are two ways to fix your issue.

The First one involved moving your state down to the Child Component i.e Header since only Header uses message state. The Other thing to keep in mind is that you don't call state updater directly in render since it will keep calling the react state updater although it won't affect your app since react internally prevents re-render if the same state is supplied to state updater again.

const [message, setMessage] = useState({});
setMessage('my message');

return (
    {message && (
        <div>
            <span>{message.text}</span>
            // This on click makes the entire app reload
            <button className='close' onClick={() => setMessage({})}>
                X
            </button>
        </div>
    )}
);

The second way which is a more reliable way when you have to pass on message to more than one component is to make use of React.memo for the component

const MyComponent = () => (
   ...
);

export default React.memo(MyComponent)

Upvotes: 1

Related Questions