Search code examples
reactjsreact-reduxreact-hooksfetch

React redux shows previous state old data before fetching new data


I use React-Redux and functional components with hooks. Whenever I send a request for fetching data from the server I set isLoading field in global state to true and then after request is done back to false. In the rendering component I either show the fetched data or a loading screen if isLoading is set to true. Now the problem is whenever I change page and then reopen the sreen with data, before showing loading screen react renders previous state data from the previous fetch for a brief moment. Hope someone can explain to me how to avoid that behavior and the reasons why it acts that way. Here's an example code of what I'm talking about. If I change routes and go back I can see the old data:

//App.js
function App() {
    return (
        <BrowserRouter>
            <Header/>
            <Route exact path='/' component={ProductList}/>
            <Route exact path='/NotHome' component={NotHome}/>
        </BrowserRouter>
    );
}
//ProductList.js
const ProductList = (props) => {

    const [Skip, setSkip] = useState(0);
    const [Limit] = useState(5);

    useEffect(() => {
        props.requestProductList(Skip, Limit);
    }, []);

    return (
        props.isLoading
            ? <h1>LOADING</h1>
            : <div>
                {props.productList && props.productList.map(product => (
                    <div key={product._id}>
                        <p>{product.productName}</p>
                        <img src={`http://localhost:5000${product.productMainPicture}`} style={{width: 200, height: 200}}/>
                    </div>
                ))}
            </div>
    )
};

const mapStateToProps = (state) => ({
    productList: state.product.productList,
    isLoading: state.product.isLoading,
});

const actionCreators = {
    requestProductList
};

export default connect(mapStateToProps, actionCreators)(ProductList);
//ProductReducer.js

const PRODUCT_SET_PRODUCT_LIST = 'PRODUCT_SET_PRODUCT_LIST';
const SET_LOADING = 'SET_LOADING';

const initialState = {
    productList: [],
    isLoading: false
}

export const ProductReducer = (state = initialState, action) => {
    switch (action.type) {
        case PRODUCT_SET_PRODUCT_LIST:
            return {
                ...state,
                productList: [...action.productList],
                productsCount: action.productsCount
            }
        case SET_LOADING:
            return {
                ...state,
                isLoading: action.isLoading
            }
        default:
            return state;
    }
};

const setProductList = (productList, productsCount) => ({type: PRODUCT_SET_PRODUCT_LIST, productList, productsCount});
const setLoading = (isLoading) => ({type: SET_LOADING, isLoading});

export const requestProductList = (skip, limit) => async (dispatch) => {
    dispatch(setLoading(true));
    try {
        const res = await productApi.requestProductList(skip, limit);
        dispatch(setProductList(res.data.products));
        dispatch(setLoading(false));
    } catch (e) {
        console.log(e);
        dispatch(setLoading(false));
    }
};

//api.js
export const productApi = {
    async requestProductList(skip, limit) {
        return await Axios.post(`http://localhost:5000/api/product/get_product_list`, {skip, limit});
    }
}

Solution

  • How about clearing the data when leaving the page and then when revisiting, everything should work as expected.

    Let me explain,

    Lets say you set the Redux state with ObjectA = {...}, then when you leave that page, objectA still exists with values, so it immediately displays those values. While the network request is asynchronous and takes time to complete the promise and update the objectA.

    To solve this, you can create a clearAction, which clears objectA when leaving the page.

    useEffect(() => 
    {
            props.requestProductList(Skip, Limit);
    
           return () =>
           {
             props.clearData()
           }
    
    
    }, []);
    
    
    /* Redux reducers*/
    
    case CLEAR_DATA:
        return {...state, objectA: null}