简体   繁体   中英

React Redux, useSelector causing component to render twice

I'm trying to create a user area for a react app,

If the user is logged in then the data is stored in redux under 'userData',

The problem I've got is that if a suer does to the user area when they are not logged in, I want the app to redirect them to another 'notloggedin' page,

I've got the below, however the problem I've got is when the route is first rendered userDetails is returning an empty object which subsequently triggers the history.push('/notloggedin') . I can see if I remove the history.push('/notloggedin') and load the component first time round it loads userDetails as an empty object, but then rerenders as a populated object. I'm really stuck on how to only cause the component to render once the object has been populated from useSelector.

import React, {useState, useEffect} from 'react';
import {useSelector, shallowEqual} from 'react-redux';

const index = () => {
    const userDetails = useSelector(state=>state.userData, shallowEqual);

useEffect(()=>{
    if(Object.keys(userDetails).length===0){
        history.push('/notloggedin')
    }
},[])
    return (
        <div>
            <h1>Member Area</h1>
            <p>Your User name us</p>
            {userDetails.username}
            ...
        </div>
    );
};

export default index;

if I remove the line const userDetails = useSelector(state=>state.userData, shallowEqual); then the component only renders once as I would expect, I can't see why the above live is causing it to render twice!

userData is populated with the below action

//login/actions.js

export const getUserData = (token) => {
    console.log('getuser data');
    return dispatch => {
        api.getUserData()
        .then(res =>
            dispatch({
                type: GET_USER_DETAILS,
                payload: res.data.user
            })
        );
    };
};

which is called in App.js

//App.js


const App = () => {
    

    const dispatch = useDispatch();

    useEffect(() => {
       
        if (localStorage.getItem("userToken")) {
            
    
    dispatch(getUserData(localStorage.getItem("userToken")));
        }
        
    }, []);

    return (
        <div className="app">
           ...
        </div>
    );
};

export default App;

Thanks!

The problem with your implementation is that you are setting your user state once components are rendered (you are setting the user state using useEffect in App.js ).

First render with the user state being null -> useEffect dispatchs the user data retrieved from local storage -> user state gets updated -> another render since you are listening to the state change.

If you want to set your user data using local storage, I recommend doing that when initialing your redux state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM