You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React待办应用登录跳转Dashboard时出现tasks未定义错误求助

React登录跳转后Dashboard报tasks未定义的问题

我基于React开发了一个简易待办事项应用,正在为其添加登录功能,期望将登录页设为首页,登录完成后跳转至Dashboard页面,但刚接触React。代码编译无报错,但提交用户名和密码后,浏览器出现如下错误:

tasks is undefined
Dashboard@http://localhost:3000/static/js/bundle.js:215:20
renderWithHooks@http://localhost:3000/static/js/bundle.js:26189:31
mountIndeterminateComponent@http://localhost:3000/static/js/bundle.js:29473:17
beginWork@http://localhost:3000/static/js/bundle.js:30769:20
callCallback@http://localhost:3000/static/js/bundle.js:15785:18
invokeGuardedCallbackDev@http://localhost:3000/static/js/bundle.js:15829:20
invokeGuardedCallback@http://localhost:3000/static/js/bundle.js:15886:35
beginWork$1@http://localhost:3000/static/js/bundle.js:35750:32
performUnitOfWork@http://localhost:3000/static/js/bundle.js:34998:16
workLoopSync@http://localhost:3000/static/js/bundle.js:34921:26
renderRootSync@http://localhost:3000/static/js/bundle.js:34894:11
performSyncWorkOnRoot@http://localhost:3000/static/js/bundle.js:34586:38
flushSyncCallbacks@http://localhost:3000/static/js/bundle.js:22622:26
flushSyncCallbacksOnlyInLegacyMode@http://localhost:3000/static/js/bundle.js:22604:9
scheduleUpdateOnFiber@http://localhost:3000/static/js/bundle.js:34116:11
dispatchSetState@http://localhost:3000/static/js/bundle.js:27217:32
./node_modules/react-router-dom/dist/index.js/BrowserRouter/setState<@http://localhost:3000/static/js/bundle.js:39391:101
push@http://localhost:3000/static/js/bundle.js:1815:15
./node_modules/react-router/dist/index.js/useNavigateUnstable/navigate<@http://localhost:3000/static/js/bundle.js:40519:61
handleSubmit@http://localhost:3000/static/js/bundle.js:841:15

我不确定该如何调试这个错误,这会不会是路由问题?注:目前没有注册或真实验证逻辑,我只想先实现登录跳转功能。

相关代码

index.js

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

const DATA = [
  {id: "todo-0", name: "Make bed", completed: true},
  {id: "todo-1", name: "Fold laundry", completed: false},
  {id: "todo-2", name: "Brush teeth", completed: false}
];

ReactDOM.render(
  <BrowserRouter>
    <App tasks={DATA} />
  </BrowserRouter>,
  document.getElementById('root')
);

Login.js

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './Login.css';
import { useNavigate } from 'react-router-dom';

async function loginUser(credentials) {
 return fetch('http://localhost:8080/login', {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json'
   },
   body: JSON.stringify(credentials)
 })
   .then(data => data.json())
}

export default function Login({ setToken }) {
  const navigate = useNavigate();
  const [username, setUserName] = useState();
  const [password, setPassword] = useState();

  const handleSubmit = async e => {
    e.preventDefault();
    const token = await loginUser({
      username,
      password
    });
    if(token) {
      setToken(token);
      navigate("/dashboard");
    }
  }

  return(
    <div className="login-wrapper">
      <h1>Please Log In</h1>
      <form onSubmit={handleSubmit}>
        <label>
          <p>Username</p>
          <input type="text" onChange={e => setUserName(e.target.value)} />
        </label>
        <label>
          <p>Password</p>
          <input type="password" onChange={e => setPassword(e.target.value)} />
        </label>
        <div>
          <button type="login">Login</button>
        </div>
      </form>
    </div>
  )
}

Login.propTypes = {
  setToken: PropTypes.func.isRequired
};

App.js

import Dashboard from './components/Dashboard/Dashboard';
import Login from './components/Login/Login';
import { Route, Routes } from "react-router-dom";
import './App.css';
import useToken from './useToken';

function App() {
  const {token, setToken} = useToken();
  
  if(!token) {
    return <Login setToken={setToken} />
  }

  return (
    <div>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </div>
  );
}

export default App;

Dashboard.js

import React, { useState, useRef, useEffect } from "react";
import Form from "./Form";
import FilterButton from "./FilterButton";
import Todo from "./Todo";
import { nanoid } from "nanoid";

function usePrevious(value) {
    const ref = useRef();
    useEffect(() => {
      ref.current = value;
    });
    return ref.current;
  }
  
  const FILTER_MAP = {
    All: () => true,
    Active: (task) => !task.completed,
    Completed: (task) => task.completed
  };
  
  const FILTER_NAMES = Object.keys(FILTER_MAP);

export default function Dashboard(props) {
    const [tasks, setTasks] = useState(props.tasks);
    const [filter, setFilter] = useState('All');

    function addTask(name) {
        const newTask = { id: `todo-${nanoid()}`, name, completed: false };
        setTasks([...tasks, newTask]);
      }
    
      function toggleTaskCompleted(id) {
        const updatedTasks = tasks.map((task) => {
          // if this task has the same ID as the edited task
          if (id === task.id) {
            // use object spread to make a new object
            // whose `completed` prop has been inverted
            return {...task, completed: !task.completed}
          }
          return task;
        });
        setTasks(updatedTasks);
      }
    
      function deleteTask(id) {
        const remainingTasks = tasks.filter((task) => id !== task.id);
        setTasks(remainingTasks);
      }
    
      function editTask(id, newName) {
        const editedTaskList = tasks.map((task) => {
        // if this task has the same ID as the edited task
          if (id === task.id) {
            //
            return {...task, name: newName}
          }
          return task;
        });
        setTasks(editedTaskList);
      }  
    
      const taskList = tasks.filter(FILTER_MAP[filter]).map((task) => (
        <Todo
          id={task.id}
          name={task.name}
          completed={task.completed}
          key={task.id}
          toggleTaskCompleted={toggleTaskCompleted}
          deleteTask={deleteTask}
          editTask={editTask}/>
      ));
      
      
      const filterList = FILTER_NAMES.map((name) => (
        <FilterButton
          key={name}
          name={name}
          isPressed={name === filter}
          setFilter={setFilter}
        />
      ));
    
      const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task';
      const headingText = `${taskList.length} ${tasksNoun}`;
      const listHeadingRef = useRef(null);
      const prevTaskLength = usePrevious(tasks.length);
    
      useEffect(() => {
        if (tasks.length - prevTaskLength === -1) {
          listHeadingRef.current.focus();
        }
      }, [tasks.length, prevTaskLength]);

    return(
        <div className="todoapp stack-large">
        <h1>Trevor's ToDo List</h1>
            <Form addTask={addTask} />
            <div className="filters btn-group stack-exception">
                {filterList}
            </div>
            <h2 id="list-heading" tabIndex="-1" ref={listHeadingRef}>
                {headingText}
            </h2>
            <ul className="todo-list stack-large stack-exception" aria-labelledby="list-heading" >
                {taskList}
            </ul>
        </div>
    );
}

问题原因及解决办法

核心问题

Dashboard组件依赖props.tasks初始化内部状态,但App.js渲染Dashboard时未传递该props。虽然index.js将DATA传给了App组件,但App没有向下传递给Dashboard,导致props.tasks为undefined,初始化的tasks state也变成undefined,后续执行tasks.filter时抛出错误。

具体修复步骤

  1. 修改App.js,传递tasks props给Dashboard
    让App组件接收tasks props,并在路由中传递给Dashboard:
function App({ tasks }) {
  const {token, setToken} = useToken();
  
  if(!token) {
    return <Login setToken={setToken} />
  }

  return (
    <div>
      <Routes>
        <Route path="/login" element={<Login setToken={setToken} />} />
        <Route path="/dashboard" element={<Dashboard tasks={tasks} />} />
      </Routes>
    </div>
  );
}
  1. 为Dashboard的props设置默认值(可选但推荐)
    给Dashboard的tasks props设置默认空数组,避免后续遗漏传参导致错误:
export default function Dashboard({ tasks = [] }) {
    const [tasksState, setTasks] = useState(tasks);
    // 其余代码保持不变
}
  1. 修复Login组件的提交按钮类型(额外优化)
    将Login组件中按钮的type="login"改为type="submit",确保表单能正常触发提交事件:
<button type="submit">Login</button>

完成以上修改后,登录跳转至Dashboard时组件就能获取到tasks数据,不会再出现undefined错误。

内容的提问来源于stack exchange,提问作者CodenameCaptainFuture

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 17:07:32