2 years ago

#66985

test-img

coltonyoung

React Context children re-rendering to initial state when updating context state

I'm having an issue where, whenever I update my context state within a hook (useEffect, useCallback, etc.), any other state updates that I make don't actually go through because the state for that component resets to initial state.

I have the following component which provides the message context, its provider, and exposes a hook to access the message state variable's value and setter:

const MessageContext = React.createContext<MessageContextProps>({
  message: {} as IMessage,
  setMessage: () => {},
});

export function MessageProvider(props: MessageProviderProps): JSX.Element {
  const [message, setMessage] = useState<IMessage>({} as IMessage);

  return <MessageContext.Provider value={{ message, setMessage }}>{props.children}</MessageContext.Provider>;
}

export function useMessage(): MessageContextProps {
  return React.useContext(MessageContext);
}

My app is wrapped in the provider:

ReactDOM.render(
  <React.StrictMode>
    <ErrorBoundary>
       <MessageProvider>
          <App />
       </MessageProvider>
    </ErrorBoundary>
  </React.StrictMode>,
  document.getElementById('root')
);

Whenever I try to update the formData in my component (setFormData(newFormData)) in the same render cycle as I update the message state (setMessage()) from MessageContext, only the message state updates. The rest of my state updates don't re-render (specifically, the formData.changedValue value in the final paragraph tag), I think because it's getting reset to initial state.

export default function App(): JSX.Element {
  const [formData, setFormData] = useState(INITIAL_STATE);
  const { message, setMessage } = useMessage();
  const [updateSuccessful, setUpdateSuccessful] = useState(false);

  const handleSubmit = useCallback(
    (event: React.FormEvent<HTMLFormElement>) => {
      event.preventDefault();
      makeSomeAPICall(formData);
      setUpdateSuccessful(true);
    },
    [formData]
  );

  useEffect(() => {
    if (updateSuccessful) {
      setFormData((current) => {
        return {
          ...current,
          changedValue: current.myField,
        };
      });
      setMessage({
        displayText: `Change Successful`,
        type: 'success',
      });
    }
  }, [updateSuccessful]);

  return (
  <>
    <form onSubmit={handleSubmit}>
      <div>
        <select id='changeMe' value={formData.myField} onChange={() => setFormData(formData.myField)}>
          <option value='Y'>Y</option>
          <option value='N'>N</option>
        </select>
        <button type='submit'>Submit</button>
    </form>
    <p>Changed Value: {formData.changedValue}</p>
  </>
  );
}

reactjs

react-hooks

render

react-context

reactjs

react-hooks

render

react-context

0 Answers

Your Answer

Accepted video resources