0

I fetched some data from my api by react-redux. My problem is that, since it is async I have to wait for the state to update its inital value in order to use them in the app. For example I have to use

products && products.length && products[n].img

syntax not to get undefined error when I try to access the fetched data. But when I use them at the first render just as

products[n].img

the app gives undefined as it should because redux fetches the data asynchronously. How can I bypass these steps so that I can use my desired state immediately?

React code

import React, { useEffect } from "react";
import {useDispatch, useSelector} from 'react-redux'
import { listPoduct } from "../actions/productActions";

const Examples = () => {

    const dispatch = useDispatch()

    const productList = useSelector(state => state.productList)
    const {loading, error, products} = productList

    useEffect(()=>{
        dispatch(listPoduct())
    },[dispatch])

    console.log(products && products.length && products[0].img)

    return(
        <div>
          ...
        </div>
    )
}

export default Examples

Action

export function listPoduct() {
    return (dispatch) => {
        const baseUrl = "/api/images"
        fetch(`${baseUrl}`)
            .then(res => res.json())
            .then(res => {
                dispatch({
                    type: PRODUCT_LIST_SUCCESS,
                    payload: res
                })
            })
    }
}

Reducer

export const productListReducer = (state = { products: [] }, action) => {
    switch (action.type) {
        case PRODUCT_LIST_REQUEST:
            return {loading:true, products:[]}
        case PRODUCT_LIST_SUCCESS:
            return {loading:false, products: action.payload}
        case PRODUCT_LIST_FAIL:
            return {loading:false, error: action.payload}
        default:
            return state
    }
}

Store

import {createStore, combineReducers, applyMiddleware} from 'redux' 
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension' 
import {productListReducer, productDetailsReducer} from './reducers/productReducer'

const reducer = combineReducers({
    productList: productListReducer,
    productDetails: productDetailsReducer
})

const initialState = {}

const middleware = [thunk]

const store = createStore(
    reducer,
    initialState,
    composeWithDevTools(applyMiddleware(...middleware))
  )

export default store 

1 Answer 1

2

The short answer is that you cannot. Sadly. Your request is asynchronous, so there's just no data available immediately. In your particular case, my advice would be to render some kind of spinner-loader conditionally (if loading is set to true) and only the loader. In this case, if you have loading set to true, you will not reach the place where you can actually read the data (you will render-and-return before). And once loading switches back to false, you can now display the data safely as the request is finished and the data is in the right place.

The same applies to the failure state (as there's also no data available if the request failed).

Here's your modified code (as an example):

const Examples = () => {

    const dispatch = useDispatch()

    const productList = useSelector(state => state.productList)
    const {loading, error, products} = productList

    useEffect(()=>{
        dispatch(listPoduct())
    },[dispatch]);

    if (loading) {
        return (<div>Loading...</div>);
    }

    if (error) {
        return (<div>Error: {error}</div>);
    }

    console.log(products && products.length && products[0].img)

    return(
        <div>
          ...
        </div>
    )
}
Sign up to request clarification or add additional context in comments.

1 Comment

@JózsefKiss you're welcome!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.