Необнаруженная ошибка выполнения в приложении React.js

У меня есть простое приложение списка дел, написанное на React, и я пытаюсь добавить к нему вход в систему. Я хочу, чтобы страница входа была целевой и перенаправлялась на панель управления после входа в систему, но я все еще новичок в 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')
);

Логин.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
};

Приложение.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>
    );
}
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
0
177
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема

Ошибка: «Задачи не определены» где-то в «Dashboard@http://localhost:3000/static/js/bundle.js:215:20». Судя по коду, состояние tasks в Dashboard не определено, потому что props.tasks не определено, потому что tasks не было передано свойство Dashboard.

<Routes>
  <Route path = "/login" element = {<Login />} />
  <Route
    path = "/dashboard"
    element = {<Dashboard />} // <-- no tasks prop passed!
  />

</Routes>
export default function Dashboard(props) {
  const [tasks, setTasks] = useState(props.tasks); // <-- undefined
  ...

В любом месте Dashboard, где есть ссылка на tasks, например. например tasks.map() или tasks.filter(), выдаст ошибку, поскольку tasks не определено.

Решение

Передайте свойство tasks, переданное в App, дочерним компонентам.

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} /> // <-- tasks passed here
  </BrowserRouter>,
  document.getElementById('root')
);

Приложение

function App({ tasks }) { // <-- access tasks prop
  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} />} // <-- pass tasks prop
        />
      </Routes>
    </div>
  );
}

Панель приборов

export default function Dashboard(props) {
  const [tasks, setTasks] = useState(props.tasks); // <-- defined 🙂
  ...

Хотя это решает проблему, я бы посоветовал переместить константу задач ближе к тому месту, где она используется. На ранних стадиях разработки можно заменить выборку данных константой, но всегда полезно подумать, какой компонент будет отвечать за выборку данных в будущем.

Alexander Wiklund 10.06.2024 22:42

@AlexanderWiklund Спасибо. Всегда полезно ограничить объем объявленных переменных, но в этом случае, если вы просите об улучшениях/оптимизации, я бы предложил реализовать/интегрировать глобальное состояние через контекст React (чтобы избежать анти-шаблона «детализация реквизита») или Redux или аналогичное решение для управления состоянием.

Drew Reese 10.06.2024 23:01

Другие вопросы по теме