Jon Harding
Jon Harding

Reputation: 4946

useContext call preventing subsequent useState update

I have a provider wrapper around some routes

<Provider>
    <Route path={ROUTES.SIGNING}><SignIn /></Route>
    <PrivateRoute path={ROUTES.PRIVATE}><Private /></PrivateRoute>
</Provider>

The Provider is simply a wrapper for a userContext

import React, { useState } from 'react';
import UserContext from '../../user.context';

let defaultUser = '';    
try {
    defaultUser = JSON.parse(localStorage.getItem('profile'));
} catch {
    defaultUser = '';
}

function Provider(props) {
    const [user, setUser] = useState(defaultUser);

    return <UserContext.Provider value={{ user, setUser }}>{props.children}</UserContext.Provider>

}

export default Provider;

My <SignIn /> Component waits for a response from a data service then 1. attempts to update the setter from userContext and then trys to update it's own useState function. It never seems to execute the internal useState function.

function SignIn() {
    const { user, setUser } = useContext(UserContext);
    const [formStatus, setFormStatus] = useState();

async function handleSubmit(e) {
    e.preventDefault();
    const result = await signin(credentials);

    setUser({ isInternal: result.isInternal, clientId: result.clientId });
    setFormStatus((curStatus) => ({ ...curStatus, state: FORMSTATUS.COMPLETED }));
}

Why would the setStatus never seem to fire? I think it's because setUser updates the Provider which is a higher level component than the child SignIn Page. Any help would be great

Upvotes: 0

Views: 44

Answers (1)

Cory House
Cory House

Reputation: 15045

You have a race condition. To resolve, you need to specify that your local state update should complete before the context state update.

Solution:

  1. Use React's functional form to set local state.
  2. Then call your context state update within the functional set state call.

This way, you know the local state has completed before you update context.

Upvotes: 1

Related Questions