【问题标题】:Redux-Saga: TypeError: Cannot read properties of undefined (reading 'data')Redux-Saga:TypeError:无法读取未定义的属性(读取“数据”)
【发布时间】:2023-11-10 07:35:01
【问题描述】:

我试图用 redux-saga 运行我的 Redux 应用程序。

基本上在我的store.js 我有以下代码:

import { applyMiddleware, createStore } from "redux";
import createSagaMiddleware from "redux-saga";
import logger from "redux-logger";

import rootReducer from "./reducers/rootReducer";
import rootSaga from "./sagas/userSagas";

const sagaMiddleware = createSagaMiddleware();

const middleware = [sagaMiddleware];

if (process.env_NODE_ENV === "development") {
  middleware.push(logger);
}

const store = createStore(rootReducer, applyMiddleware(...middleware));

sagaMiddleware.run(rootSaga);

export default store;

我的usersApi.js 看起来像这样:

import axios from "axios";

export const loadUsersApi = async () => {
  await axios.get("http://localhost:5000/users");
};

这是我的userSagas

import * as type from "../actionType";

import {
  take,
  takeEvery,
  takeLatest,
  put,
  all,
  delay,
  fork,
  call,
} from "redux-saga/effects";

import { loadUsersSuccess, loadUsersError } from "../actions/userAction";
import { loadUsersApi } from "../services/userApi";

export function* onLoadUsersStartAsync() {
  try {
    const response = yield call(loadUsersApi);
    if (response.status === 200) {
      yield delay(500);
      yield put(loadUsersSuccess(response.data));
    }
  } catch (error) {
    yield put(loadUsersError(error.response.data));
  }
}

export function* onLoadUsers() {
  yield takeEvery(type.LOAD_USERS_START, onLoadUsersStartAsync);
}

const userSagas = [fork(onLoadUsers)];

export default function* rootSaga() {
  yield all([...userSagas]);
}

当我在我加载和调度数据的HomePage.js 文件上运行此程序时:

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { loadUsersStart } from "../redux/actions/userAction";

export default function HomePage() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(loadUsersStart());
  }, [dispatch]);

  return (
    <div>
      <h1>Home</h1>
    </div>
  );
}

它给了我这个错误:

[HMR] Waiting for update signal from WDS...
index.js:1 TypeError: Cannot read properties of undefined (reading 'data')
    at onLoadUsersStartAsync (userSagas.js:25)
    at onLoadUsersStartAsync.next (<anonymous>)
    at next (redux-saga-core.esm.js:1157)
    at currCb (redux-saga-core.esm.js:1251)

index.js:1 The above error occurred in task onLoadUsersStartAsync
    created by takeEvery(LOAD_USERS_START, onLoadUsersStartAsync)
    created by onLoadUsers
    created by rootSaga
Tasks cancelled due to error:
takeEvery(LOAD_USERS_START, onLoadUsersStartAsync)

我不确定是什么导致了这个错误,但即使是我的应用程序的记录器也没有显示正在调度的操作。

知道如何解决这个问题吗?

【问题讨论】:

    标签: reactjs redux redux-saga


    【解决方案1】:

    你需要从

    返回promise
    export const loadUsersApi = () => {
      return axios.get("http://localhost:5000/users");
    };
    

    也许下次尝试使用打字稿。它会防止你犯类似的错误

    【讨论】:

    • 你知道为什么我的 redux 记录器不记录这样的操作吗? prnt.sc/1y2betd
    【解决方案2】:

    试试这个

    try {
        const response = yield call(await loadUsersApi);
        if (response.status === 200) {
          yield delay(500);
          yield put(loadUsersSuccess(response.data));
        }
      } catch (error) {
        yield put(loadUsersError(error.response.data));
      }
    

    【讨论】:

      【解决方案3】:

      或者,您可以在不使用redux-saga 的情况下构建您的redux

      这是我通常的设置方式:-

      A.减速机
      • /slices/auth.js
      import { createSlice } from '@reduxjs/toolkit'
      
      const initialState = {
        users: [],
        loading: false,
        success: false,
        error: false,
        message: ''
      }
      
      export const usersSlice = createSlice({
        name: 'users',
        initialState,
        // // The `reducers` field lets us define reducers and generate associated actions
        reducers: {
          setUsers: (state, action) => {
            // Redux Toolkit allows us to write "mutating" logic in reducers. It
            // doesn't actually mutate the state because it uses the Immer library,
            // which detects changes to a "draft state" and produces a brand new
            // immutable state based off those changes
            state.users = action.payload
          },
          setLoading: (state, action) => {
            state.loading = action.payload
          },
          setSuccess: (state, action) => {
            state.success = action.payload.status
            state.message = action.payload.message
          },
          setError: (state, action) => {
            state.error = action.payload.status
            state.message = action.payload.message
          }
        }
      })
      
      export const { setUsers, setLoading, setSuccess, setError, setMessage } = usersSlice.actions;
      
      // The function below is called a selector and allows us to select a value from
      // the state. Selectors can also be defined inline where they're used instead of
      // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
      export const selectUsers = (state) => state.users.users
      export const selectLoading = (state) => state.users.loading
      export const selectSuccess = (state) => state.users.success
      export const selectError = (state) => state.users.error
      export const selectMessage = (state) => state.users.message
      
      export default usersSlice.reducer;
      

      B.商店

      • store.js
      import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
      import usersReducer from '../slices/users'
      
      export const store = configureStore({
        reducer: {
          users: usersReducer
        },
        middleware: getDefaultMiddleware({
          serializableCheck: false
        })
      })
      
      C.应用组件
      import { Provider } from 'react-redux'
      import { store } from '../app/store'
      
      export default function App() {
        return {
          <>
            <Provider store={store}>
              {/* your content... */}    
            </Provider>
          </>
        }
      }
      
      D.组件(使用 redux 的地方)
      import { useContext, useEffect } from 'react'
      import { useDispatch, useSelector } from 'react-redux'
      import { selectUsers, selectLoading, selectSuccess, selectError, selectMessage, setUsers, setLoading, setSuccess, setError } from '../slices/users'
      import axios from 'axios'
      
      export default function HomePage() {
        const dispatch = useDispatch()
        const users = useSelector(selectUser)
        const loading = useSelector(selectLoading)  
        const success = useSelector(selectSuccess)
        const error = useSelector(selectorError)
        const message = useSelector(selectorMessage)
      
        useEffect(() => {
          async function init() {
            dispatch(setLoading(true))
            const response = await axios.get('http://localhost:5000/users')
            
            if(response?.status == 200) {
              dispatch(setUsers(response?.data?.data))
              dispatch(setSuccess({ status: true, message: 'Successfully get users data.' }))
            } else {
              dispatch(setError({ status: true, message: 'Failed to get data.' }))
            }
      
            dispatch(setLoading(false))
          }
          return init()
        }, [])
      
        return {
          <>
            {user}
          </>
        }
      }
      

      【讨论】:

        最近更新 更多