How to pass state between all child components after user authorization?

Viewed 52

The server is returning user data. In particular, I am interested in his rights. With these rights, I will render the components. I am thinking of passing this data to components and doing some checks. All routes are in the index.js

import {Notfound404} from './components/404/404NotFound'
import {UserContext} from './UserContext'

const Router = () => {

    const [user, setUser] = useState(null)

    return (
        <React.StrictMode>
            <UserContext.Provider value={{user, setUser}}>
                <CookiesProvider>
                    <BrowserRouter>
                        <Switch>
                            <Route exact path={'/'} component={Auth}/>
                            <Route exact path={'/settings'} component={Settings}/>
                            <Route exact path={'/event-logs'} component={EventLog}/>
                            <Route component={Notfound404} status={404}/>
                        </Switch>
                    </BrowserRouter>
                </CookiesProvider>
            </UserContext.Provider>
        </React.StrictMode>
    )
}

setting user data in Auth.js

import {UserContext} from '../../UserContext'

export const Auth = () => {

    const {user, setUser} = useContext(UserContext)

    const [username, setUsername] = useState('')
    const [password, setPassword] = useState('')
    const [token, setToken] = useCookies(['qr-token'])

    useEffect(() => {
        if (token['qr-token']) window.location.href = '/settings'
    }, [token])

    const credentials = {
        username: username,
        password: password
    }

    const sendCredentials = () => {
        axios.post(`http://127.0.0.1:8000/api/v1/auth/`, credentials, {
            headers: {
                'Content-type': 'application/json',
            }
        })
            .then(resp => {
                setToken('qr-token', resp.data.token);
                setUser(resp.data)
            })
            .catch(error => console.log(error.response))
    }

    return !token['qr-token'] &&
        <div className={styles.authContainer}>
            <label htmlFor={'username'}>Username</label>
            <input id={'username'} type={'text'} placeholder={'username'}
                   onChange={evt => setUsername(evt.target.value)}
            />
            <label htmlFor={'password'}>Password</label>
            <input id={'password'} type={'password'} onChange={evt => 
                setPassword(evt.target.value)}/>
            <button onClick={sendCredentials}>Sign In</button>
        </div>
    )
}

In the index.js I am getting user data after authorization. But in settings.js use data is null

export const Settings = () => {

    const {user, setUser} = useContext(UserContext)
    console.log(user) # null
....
}

Probably I am doing something wrong and I am asking for your help. Thanks.

3 Answers

I think a refresh is happening while redirecting to the setting page. That reload cause the context value to get cleared (simply, because the value of the context is coming from a state and that state gets lost as a result of refresh). To fix this, you can store the auth data data in localStorage and assign the context value to it whenever it gets null.

As Saba indicated, the issue is about refreshing the page which clears the context. This is done by these lines:

useEffect(() => {
    if (token['qr-token']) window.location.href = '/settings'
}, [token])

Don't use the usual redirect methods in Single Page Applications. Instead, use

const history = useHistory();
useEffect(() => {
    if (token['qr-token'])
        history.push('/settings');
}, [token])

This won't cause a reload of the page and therefore keeps your context alive.

useContext() provides a dispatch function that has been passed down from the Store component. The dispatch function accepts two arguments, a type of action, and a payload for updating the global state. The useContext() function will then return an updated global state.

const { state, dispatch } = useContext(MyContext);

In your code setUser must be triggered with the parameter contains action type and payload.

For example.

setUser({
    type: "SET_USER",
    payload: resp.data
  })
Related