3

i'm trying to use some data fetched by axios inside initialState so I can use it with useReducer later on. but as soon as I pass it as the initialState of my second argument it returns an empty array with no objects. please help... :(

 const [prods, setProds] = useState([]);
  const getProds = () => {
    axios.get(`API`).then((res) => {
      const data = res.data;
      setProds(data.products);
    });
  };


  const result = prods.map(function (el) {
    const quantity = Object.assign({}, el);
    quantity.count = 0;
    return quantity;
  });
  
  const initialState = {
    products: [result],
  };
  useEffect(() => {
    getProds();
  }, []);

console.log(initialState);

  const [countState, dispatch] = useReducer(counterReducer, initialState);

  console.log(countState);

and the result from the first and second log looks like :

useReducer empties initialState

the result from API call after adding the count to each looks something like this :

enter image description here

and here's the code for my reducer, I checked it many times but seems fine :

export const counterReducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return {
        ...state,
        products: state.products.map((product) =>
          product.id === action.id
            ? { ...product, count: product.count + 1 }
            : product
        ),
      };

    case "decrement":
      return {
        ...state,
        products: state.products.map((product) =>
          product.id === action.id
            ? {
                ...product,
                count: product.count !== 1 ? product.count - 1 : 1,
              }
            : product
        ),
      };
    default:
      return state;
  }
};

  • In the API response it looks like `products` is an array with one entry, and the entry is another array with 12 entries. So to iterate the products you’d have to do `products[0].map(...)` instead of `products.map(...)`. (This seems like an odd structure for the API to return if you ask me.) – ray Sep 25 '20 at 12:21

1 Answers1

5

useReducer consumes initialState only on first render(before axios finished), on all the subsequent rerenders it will just return the state from the memory cell. What you want to do is to call useReducer at the top

const [countState, dispatch] = useReducer(counterReducer, {products: []});

and then call dispatch in your getProds

const getProds = () => {
    axios.get(`API`).then((res) => {
      const data = res.data;
      setProds(data.products);
      dispatch({ type: "PRODUCTS_FETCHED", payload: data.products});
    });
  };

And also add according case in your reducer to set the state.

export const counterReducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return {
        ...state,
        products: state.products.map((product) =>
          product.id === action.id
            ? { ...product, count: product.count + 1 }
            : product
        ),
      };

    case "decrement":
      return {
        ...state,
        products: state.products.map((product) =>
          product.id === action.id
            ? {
                ...product,
                count: product.count !== 1 ? product.count - 1 : 1,
              }
            : product
        ),
      };
    case "PRODUCTS_FETCHED":
      return {
        ...state,
        products: action.payload
      };
    default:
      return state;
  }
};