【问题标题】:Uncaught TypeError: Cannot read property 'map' of undefined in Reactjs - redux未捕获的类型错误:无法读取 Reactjs 中未定义的属性“地图”-redux
【发布时间】:2021-08-18 03:56:58
【问题描述】:

我正在使用 MongoDB、Express、React、Node (MERN) 项目。我在更改 redux 文件以实现 Material UI/core

的加载效果后遇到“无法读取未定义的属性 'map' 的问题

我尝试通过 useSelector 以不同的方式访问数据,即使是使用 shallowEqual 方法。我也尝试在 DashBoardAdmin 中调用 getStudents()。同时也尝试使用 useEffect 来调度 (getStudents()) 与依赖数组。到目前为止,一切都没有奏效。然后尝试在 chrome 的检查部分进行调试,我发现在第一次重新加载页面时,恰好在 action.payload 上从后端获取数据,但它无法整体填充到状态中。这可能是 useSelector 获取空数组并提供“无法读取未定义的属性 'map'

的原因

我认为,在引入 state 中的对象之后,reducers 的students.js 文件出现了问题。我正在尽力调试。

我的 index.js 文件:

import React from "react";
import ReactDOM from "react-dom";
import "./Styles/index.css";
import App from "./App";
import { Provider } from "react-redux";
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";

import { reducers } from "./redux/reducers/index";

const composeEnhancers =
  typeof window === "object" && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
    ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
    : compose;

const enhancer = composeEnhancers(compose(applyMiddleware(thunk)));
const store = createStore(reducers, enhancer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

我的 app.js 文件:

import React, { useEffect, useState } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import "./Styles/App.css";
import AdminSignIn from "./Pages/AdminSignIn";
import DashBoardAdmin from "./Pages/Admin";
import NavbarAdmin from "./Navbars/NavbarAdmin";
import BottomNavbar from "./Navbars/bottomNavbar";
import { useDispatch } from "react-redux";
import { Typography } from "@material-ui/core";
import { NotFound } from "./Not_Found/NotFound";
import { getStudents } from "./redux/actions/studentAction";

function App() {
  const user = JSON.parse(localStorage.getItem("account"));
  const dispatch = useDispatch();

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

  return (
    <>
      <Router>
      {user?.result?._id ? (
        <NavbarAdmin />
      ) : (
        <Typography variant="h2">{"Fetch"} Organization Name</Typography>)}
        <Switch>
          <Route path="/" exact>
            <AdminSignIn />
          </Route>
          <Route path="/dashboard" exact>
            <DashBoardAdmin />
          </Route>
 
          <Route >
            <NotFound />
          </Route>
        </Switch>
        <BottomNavbar />
      </Router>
    </>
  );
}

export default App;

我的 DashBoardAdmin.js 文件:

import { Box, Button, Card, CardHeader, Chip, CircularProgress, Divider, Table, TableBody, TableCell, TableHead, TableRow, TableSortLabel, Tooltip} from "@material-ui/core";
import { Link } from 'react-router-dom'
import ArrowRightIcon from "@material-ui/icons/ArrowRight";
import moment from "moment";
import PerfectScrollbar from "react-perfect-scrollbar";
import { useSelector } from "react-redux";

const DashBoardAdmin = () => {
  const { students, isLoading } = useSelector((state) => state.students);

  return (
    <div className="padding-grid">
      <Card>
        <CardHeader title="Latest updates on students" />
        <Divider />
        <PerfectScrollbar>
          <Box sx={{ minWidth: 800 }}>
            <Table>
              <TableHead>
                <TableRow>
                  <TableCell>Roll Number</TableCell>
                  <TableCell>Name of student</TableCell>
                  <TableCell sortDirection="desc">
                    <Tooltip enterDelay={300} title="Sort">
                      <TableSortLabel active direction="desc">
                        Date of Admission
                      </TableSortLabel>
                    </Tooltip>
                  </TableCell>
                  <TableCell>Status</TableCell>
                </TableRow>
              </TableHead>
              {isLoading ? (
                <CircularProgress />
              ) : (
                <TableBody>
                  {students.map((stu) => (
                    <TableRow hover key={stu.id}>
                      <TableCell>{stu.rollNumber}</TableCell>
                      <TableCell>{stu.firstName}  {" "} {stu.lastName} + {" "} {stu.surname}</TableCell>
                      <TableCell>
                        {moment(stu.createdAt).format("DD/MM/YYYY")}
                      </TableCell>
                      <TableCell>
                        <Chip color="primary" label={stu.status} size="small" />
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              )}
            </Table>
          </Box>
        </PerfectScrollbar>
        <Box
          sx={{
            display: "flex",
            justifyContent: "flex-end",
            p: 2,
          }}>
            <Link to="/students-info">
          <Button
            color="primary"
            endIcon={<ArrowRightIcon />}
            size="small"
            variant="text">
            View all
          </Button>
          </Link>
        </Box>
      </Card>
    </div>
  );
};

export default DashBoardAdmin;

我的 redux studentAction.js 文件:

import { FETCH_STUDENTS, START_LOADING, END_LOADING } from "../constants/actionTypes";
import * as api from "../api/index.js";

export const getStudents = () => async (dispatch) => {
  try {
    dispatch({ type: START_LOADING })
    const { data } = await api.fetchStudents();
    dispatch({ type: FETCH_STUDENTS, payload: data });
    dispatch({ type: END_LOADING})
  } catch (error) {
    console.log(error);
  }
};

我的 API index.js 文件:

import axios from "axios";

const API = axios.create({ baseURL: "http://localhost:5000" });

API.interceptors.request.use((req) => {
  if (localStorage.getItem("account")) {
    req.headers.Authorization = `Bearer ${
      JSON.parse(localStorage.getItem("account")).token
    }`;
  }

  return req;
});

export const fetchStudents = () => API.get("/students-info");

students.js 的减速器,最好的猜测是这里出了点问题,或者它是在我包含 isLoading 之后开始的:

import { FETCH_STUDENTS, START_LOADING, END_LOADING } from "../constants/actionTypes";

function students(state = { isLoading: true, students: [] }, action) {
  switch (action.type) {
    case START_LOADING:
      return { ...state, isLoading: true };
    case END_LOADING:
      return { ...state, isLoading: false };
  
    case FETCH_STUDENTS:
      return { ...state, students: action.payload.data };
    
    default:
      return state;
  }
}

export default students;

index.js 合并reducer文件:

import { combineReducers } from "redux";

import students from "./students";
import auth from "./auth";

export const reducers = combineReducers({ students, auth });

我得到的错误是:

Uncaught TypeError: Cannot read property 'map' of undefined
    at DashBoardAdmin (DashBoardAdmin.js:51)
    at renderWithHooks (react-dom.development.js:14985)
    at updateFunctionComponent (react-dom.development.js:17356)
    at beginWork (react-dom.development.js:19063)
    at HTMLUnknownElement.callCallback (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)
    at beginWork$1 (react-dom.development.js:23964)
    at performUnitOfWork (react-dom.development.js:22776)
    at workLoopSync (react-dom.development.js:22707)
    at renderRootSync (react-dom.development.js:22670)
    at performSyncWorkOnRoot (react-dom.development.js:22293)
    at react-dom.development.js:11327
    at unstable_runWithPriority (scheduler.development.js:468)
    at runWithPriority$1 (react-dom.development.js:11276)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11322)
    at flushSyncCallbackQueue (react-dom.development.js:11309)
    at batchedUpdates$1 (react-dom.development.js:22387)
    at Object.notify (Subscription.js:19)
    at Subscription.notifyNestedSubs (Subscription.js:90)
    at Subscription.handleChangeWrapper (Subscription.js:95)
    at Object.dispatch (redux.js:297)
    at dispatch (<anonymous>:3856:17)
    at index.js:11
    at dispatch (redux.js:659)
    at studentAction.js:35

还有一个错误:

Warning: validateDOMNesting(...): <div> cannot appear as a child of <table>.
    at div
    at CircularProgress (http://localhost:4000/static/js/vendors~main.chunk.js:80761:23)
    at WithStyles (http://localhost:4000/static/js/vendors~main.chunk.js:119309:31)
    at table
    at Table (http://localhost:4000/static/js/vendors~main.chunk.js:102171:23)
    at WithStyles (http://localhost:4000/static/js/vendors~main.chunk.js:119309:31)
    at div
    at StyledComponent (http://localhost:4000/static/js/vendors~main.chunk.js:119080:28)
    at div
    at ScrollBar (http://localhost:4000/static/js/vendors~main.chunk.js:231982:5)
    at div
    at Paper (http://localhost:4000/static/js/vendors~main.chunk.js:94231:23)

我通过在 students.js 文件中使用简单的 redux 语法从后端获取数据:

import { FETCH_STUDENTS } from "../constants/actionTypes";
export default (students = [], action) => {
   switch (action.type) {
     case FETCH_STUDENTS:
       return action.payload;
 default:
       return students;
   }
 };

需要另一种方法来实现 isLoading 或 START_LOADING / END_LOADING 向 UI 分派

【问题讨论】:

  • 快速修复:如果studentsundefined {(students || []).map((stu) =&gt; (,则使用空数组
  • @LindaPaiste 是的,这个方法可以帮助我至少看到 UI 部分,错误停止在屏幕上弹出,但数据仍然没有更新到 redux 存储中。感谢上述方法。如果您能解决用数据填充状态的任何问题,我们将不胜感激。
  • 我怀疑reducer中的“students: action.payload.data”应该是“students: action.payload”。但这取决于您的 API 响应的格式。
  • @LindaPaiste 是的尝试过,但仍然返回空数组。但是我通过实现 export default (students = [], action) => { switch (action.type) { case FETCH_STUDENTS: return action.payload; 来获取数据。默认:返回学生; } };但同样,无法实现 START_LOADING 和 END_LOADING。任何建议或者添加 START_LOADING

标签: javascript reactjs redux react-redux react-router-dom


【解决方案1】:

在看到您的 app.js 后,我看到您正在使用 thunk,我认为您必须使用函数而不是直接传递对象。

我还添加了一个操作来获取错误以防万一

studentAction.js

import {
 FETCH_STUDENTS,
 START_LOADING,
 END_LOADING,
} from "../constants/actionTypes";
import * as api from "../api";

const startLoading = () => {
 return {
  type: START_LOADING,
 };
};

const fetchStudents = (data) => {
 return {
  type: FETCH_STUDENTS,
  data,
 };
};

const endLoading = (error) => {
 return {
  type: END_LOADING,
  error,
 };
};

export const getStudents = () => {
 return async (dispatch) => {
  dispatch(startLoading());

  try {
   const { data } = await api.fetchStudents()
 
   dispatch(fetchStudents(data))

  } catch (error) {
   dispatch(endLoading(error.message));
  }
 };
};

students.js

import {
 FETCH_STUDENTS,
 START_LOADING,
 END_LOADING,
} from "../../constants/actionTypes";

const initialState = {
 isLoading: false,
 error: "",
 students: [],
};

function students(state = initialState, action) {
 switch (action.type) {
  case START_LOADING:
   return { ...state, isLoading: true };
  case END_LOADING:
   return { ...state, isLoading: false, students: [], error: action.error };

  case FETCH_STUDENTS:
   return {
    ...state,
    isLoading: false,
    students: action.data,
   };

  default:
   return state;
 }
}

export default students;

你的 app.js 和 index.js 对我来说是正确的

在你的 Admin.js 中你可以使用这个条件

import { useSelector } from "react-redux";

const DashBoardAdmin = () => {
 const { students, isLoading, error } = useSelector((state) => state.students);

 return (
  <>
   {error === "" && (
    <div className="padding-grid">
     ...
     {isLoading || !students.length > 0 ? (
      <h1>loading...</h1>
     ) : (
      <div>
       {students.map((stu) => (
        <p key={stu.id}>{JSON.stringify(stu)}</p>
       ))}
      </div>
     )}
     ...
    </div>
   )}
  </>
 );
};

export default DashBoardAdmin;

【讨论】:

  • 我已经制作了默认的 index.js 文件来渲染 app.js。接下来将添加上面的原始查询。它与您建议的相似
  • 我通过以下方式获取数据:export default (students = [], action) => { switch (action.type) { case FETCH_STUDENTS: return action.payload;默认:返回学生; } };请帮助我在 students.js 文件中实现 START_LOADING 或 isLoading 替代方法
  • 嗨@Clinto_92_Abraham 我刚刚编辑了我的答案,希望对您有所帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-09
  • 1970-01-01
  • 1970-01-01
  • 2016-08-09
  • 1970-01-01
  • 1970-01-01
  • 2017-04-25
相关资源
最近更新 更多