Search code examples
javascriptreactjsreact-reduxredux-toolkit

useSelector() is showing promise instead of updated value from store


I am using react-redux with @reduxjs/toolkit for global state management and in my store I have sliceOne with initial state and reducer. I am trying to use the state value in a component with useSelector() but it is showing promise instead of updated value. I dispatch action in that component.

store/sliceOne.js

import { createSlice } from "@reduxjs/toolkit";

const sliceOne = createSlice({
  name: "sliceOne",
  initialState: {
    firstName: "first",
    lastName: "last",
    age: 10,
    product: {},
  },
  reducers: {
    async action1(state) {
      const res = await fetch(`https://fakestoreapi.com/products/2`);
      const data = await res.json();
      state.product = data;
    },
  },
});

export const sliceOneActions = sliceOne.actions;

export default sliceOne;

component

import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { sliceOneActions } from "../store/slice-one";

const Compo = () => {
  const dispatch = useDispatch();

  useEffect(dispatch(sliceOneActions.action1()), []); // without calling this getting state value but with this getting promise

  let data = useSelector((state) => state);
  console.log(data);  //getting promise

  return <div></div>;
};

export default Compo;

Solution

  • Issues

    1. Reducer functions are to be considered pure, synchronous functions. If you need to run some asynchronous logic then you need to handle this in an asynchronous action, a.k.a. Thunk.
    2. The component is incorrectly using the useEffect hook by directly calling dispatch in place of the useEffect hook callback function.

    Solution

    Move the asynchronous logic from the reducer function into an asynchronous action.

    1. Create an asynchronous Thunk function that makes the GET request and returns the fetched data.
    2. Add a case reducer in the sliceOne slice's extraReducers to handled a fulfilled action1 action Promise.
    import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
    
    const action1 = createAsyncThunk(
      "sliceOne/action1",
      async () => {
        const res = await fetch("https://fakestoreapi.com/products/2");
        const data = await res.json();
        return data;
      }
    );
    
    const sliceOne = createSlice({
      name: "sliceOne",
      initialState: {
        firstName: "first",
        lastName: "last",
        age: 10,
        product: {},
      },
      extraReducers: builder => {
        builder
          .addCase((action1.fulfilled, (state, action) => {
            state.product = action.payload;
          }));
      },
    });
    
    export const sliceOneActions = {
      ...sliceOne.actions,
      action1,
    };
    
    export default sliceOne;
    

    Fix the UI to correctly use the useEffect hook to dispatch the action1 action.

    import { useEffect } from "react";
    import { useDispatch, useSelector } from "react-redux";
    import { sliceOneActions } from "../store/slice-one";
    
    const Compo = () => {
      const dispatch = useDispatch();
    
      useEffect(() => {
        dispatch(sliceOneActions.action1());
      }), []);
    
      const data = useSelector((state) => state);
    
      ...
    };