Rabner Casandara
Rabner Casandara

Reputation: 151

how to call useSelector Hook only when the data is available

I am making a API call in useEffect Hook and then saving the data in redux store. From redux store I am storing data in my browser local storage by making a useSelector call to get data from redux store.

How to make useSelector to call only once the data is ready from API.

import React, { useEffect, useState } from "react";
import Navbar from "./Navbar";
import Footer from "./Footer";
import HomeMenu from './HomeMenu';
import { fetchingInPending, fetchingSuccess, fetchingFailed } from './HomeSlice';
import { useSelector, useDispatch } from 'react-redux';

const Home = () => {
  const dispatch = useDispatch();
  useEffect(() => {
    (async () => {
      dispatch(fetchingInPending());
      const response = await fetch("https://localhost:44316/api/auth/getuser", {
        headers: {
          "Content-Type": "application/json"
        },
        credentials: "include",
      });
      
      if(response.status === 200){
        const content = await response.json();
        dispatch(fetchingSuccess({name: content.name, role: content.role}));
      }
      else{
        dispatch(fetchingFailed());
      }
    })();
  },[]);

  localStorage.setItem('user', JSON.stringify(useSelector(state => state.userDetails)));


  const user = localStorage.getItem('user');

  console.log(user);

  return (
    <React.Fragment>
    <h1>Home</h1>
    </React.Fragment>
)};

HomeSlice

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

export const homeSlice = createSlice({
  name: "userDetails",
  initialState: {
    name: "",
    role: "",
    isLoading: false
  },
  reducers: {
    fetchingInPending: (state) => {
      state.isLoading = true;
    },
    fetchingSuccess: (state, action) => {
      state.name = action.payload.name;
      state.role = action.payload.role;
      state.isLoading = false;
      state.error = "";
    },
    fetchingFailed: (state, action) => {
      state.isLoading = false;
      state.error = action.payload.error;
    },
  },
});

export const { fetchingInPending, fetchingSuccess, fetchingFailed } = homeSlice.actions;

export default homeSlice.reducer;

On my browser console I am getting data after three calls.

enter image description here

Index.js contain:

Index.js code:

import React from 'react';
import ReactDOM from 'react-dom';
import './style/index.css';
import App from './App';
import { BrowserRouter} from 'react-router-dom';
import store from './store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
    <Provider store={store}>
      <App />
    </Provider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

Upvotes: 1

Views: 7587

Answers (1)

Drew Reese
Drew Reese

Reputation: 203198

I think @slideshowp2 is correct, but for incorrect reasoning. There are no asynchronous actions being dispatched (i.e. thunks or similar), so there are no returned Promises to be fulfilled.

Your code

  1. Logs the initial state
  2. Logs the state after dispatch(fetchingInPending());
  3. Logs the third state update after dispatch(fetchingSuccess({ name: content.name, role: content.role }));

You can't conditionally call React hooks, so your code is correctly persisting each state update to localStorage.

If you want to conditionally persist data to localStorage then place that logic in another useEffect hook with a dependency on the redux state. The condition is ensuring that the values from your redux store have been populated.

const userDetails = useSelector(state => state.userDetails);

...

useEffect(() => {
  const { name, role } = userDetails;
  if (name && role) {
    localStorage.setItem('user', JSON.stringify({ name, role }));
  }
}, [userDetails]);

Alternatively you can just apply the localStorage persistence right in the existing useEffect. Here you persist to localStorage only when also dispatching the success action

useEffect(() => {
  (async () => {
    dispatch(fetchingInPending());
    const response = await fetch("https://localhost:44316/api/auth/getuser", {
      headers: {
        "Content-Type": "application/json"
      },
      credentials: "include",
    });
  
    if(response.status === 200){
      const { name, role } = await response.json();
      const userDetails = { name, role };

      dispatch(fetchingSuccess(userDetails)); // <-- dispatch user details
      localStorage.setItem(                   // <-- persist user details
        'user',
        JSON.stringify(userDetails)
      );
    }
    else{
      dispatch(fetchingFailed());
    }
  })();
},[]);

Upvotes: 0

Related Questions