как вызвать хук useSelector только при наличии данных

#javascript #reactjs #redux #react-redux #react-hooks

Вопрос:

Я выполняю вызов API в крючке useEffect, а затем сохраняю данные в хранилище redux. Из магазина redux я сохраняю данные в локальном хранилище браузера, выполнив вызов useSelector для получения данных из магазина redux.

Как заставить useSelector вызывать только после того, как данные будут готовы из 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>
)};
 

Домашняя мокрица

 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;
 

На консоли моего браузера я получаю данные после трех звонков.

введите описание изображения здесь

Index.js содержать:

 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')
);
 

Комментарии:

1. вы не можете условно вызывать крючки. Ознакомьтесь с правилами здесь . Вместо этого вы можете использовать optional chaining , когда получите состояние на useSelector таких: const user = useSelector(state => state.user?.name?.email) . вы можете прочитать о дополнительной цепочке здесь .

2. Я попробовал твой код. Я не обнаружил никаких проблем.. Я думаю, что эта проблема возникает не из-за useSelector. пожалуйста, проверьте index.js. На вашем скриншоте изображена проблема в index.js.. пожалуйста, опубликуйте свой index.js код.

3. обновлено с index.js код

4. В этом есть смысл. Существует три состояния: начальное состояние, ожидающее состояние, состояние выполнения/отклоненное состояние. Вы можете визуализировать разные вещи на основе этих состояний. Итак, что ты хочешь сделать?

Ответ №1:

Я думаю, что @slideshowp2 верен, но для неверных рассуждений. Не выполняется никаких асинхронных действий (т. Е. громких или подобных), поэтому нет возвращенных обещаний, которые должны быть выполнены.

Ваш код

  1. Регистрирует начальное состояние
  2. Регистрирует состояние после dispatch(fetchingInPending());
  3. Регистрирует третье обновление состояния после dispatch(fetchingSuccess({ name: content.name, role: content.role }));

Вы не можете условно вызывать крючки реакции, поэтому ваш код правильно сохраняет каждое обновление состояния в localStorage.

Если вы хотите условно сохранить данные в локальном хранилище, поместите эту логику в другой useEffect хук с зависимостью от состояния redux. Условие состоит в том, чтобы убедиться, что значения из вашего хранилища redux были заполнены.

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

...

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

В качестве альтернативы вы можете просто применить сохранение локального хранилища прямо в существующем useEffect . Здесь вы сохраняетесь в локальном хранилище только при отправке действия «Успех»

 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());
    }
  })();
},[]);