Reputation: 151
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.
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
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
dispatch(fetchingInPending());
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