反应挂钩-TypeError:无法设置未定义的属性'isCompleted'

时间:2020-03-10 05:03:30

标签: javascript reactjs react-hooks jsx react-context

在待办事项应用中不断出现类型错误。我正在尝试将isComplete属性设置为true。

错误: TypeError: Cannot set property 'isCompleted' of undefined

我的complete_todo.js文件:

import { TodoContext } from './todo_ctx'; 


const MarkComplete = (index) => {
    const [tasks, setTodos] = useContext(TodoContext);

        const newTask = [...tasks];
        newTask[index].isCompleted = true;
        setTodos(newTask);

    return (
        <button onClick={MarkComplete}>Mark Complete</button>
    );
}

export default MarkComplete;

我的todo_ctx.js文件:

import React, { useState, createContext} from 'react';

export const TodoContext = createContext();

export const TodoProvider = (props) => { 
    const [tasks, setTodos] = useState([
        {
            id: 1,
            task: 'Learn React',
            description: 'Build a todo app in React',
            isCompleted: false
        },
        {
            id: 2,
            task: 'Learn Node',
            description: 'Build a Node API',
            isCompleted: false
        },
        {
            id: 3,
            task: 'Learn Flutter',
            description: 'Complete Flutter Course',
            isCompleted: false
        }
    ]);

    return (
        <TodoContext.Provider value={[tasks, setTodos]}>
            {props.children}
        </TodoContext.Provider>
    )
}

我是React的新手,在这里我缺少什么吗?

3 个答案:

答案 0 :(得分:0)

您的代码中几乎没有错误。

  1. 使用者应该是上下文提供者的子代。
  2. 直接在组件主体中更新状态,您应该在hookfunction内部更新状态。
  3. 单击按钮时,您正在调用错误的组件本身。
  4. 相反,如果传递index,则可以传递任务。

我对您的代码进行了一些更改,请尝试。

import React, { useState, createContext, useContext, Component } from 'react';
import './App.css';

export const TodoContext = createContext();
export const TodoProvider = (props) => {
  const [tasks, setTodos] = useState([
    {
      id: 1,
      task: 'Learn React',
      description: 'Build a todo app in React',
      isCompleted: false
    },
    {
      id: 2,
      task: 'Learn Node',
      description: 'Build a Node API',
      isCompleted: false
    },
    {
      id: 3,
      task: 'Learn Flutter',
      description: 'Complete Flutter Course',
      isCompleted: false
    }
  ]);
  return (
    <TodoContext.Provider value={[tasks, setTodos]}>
      <MarkComplete />
    </TodoContext.Provider>
  )
}

const MarkComplete = (props) => {
  const [tasks, setTodos] = useContext(TodoContext);

  function todoHandler(taskId) {
    const newTask = tasks.map(task => {
      if (task.id === taskId) {
        task.isCompleted = !task.isCompleted;
      }
      return task;
    });
    setTodos(newTask);
  }

  return (
    <div >
      <table>
        <thead>
          <tr>
            <th>Task Id</th>
            <th>Task</th>
            <th>isCompleted</th>
            <th>Action</th>
          </tr>
        </thead>
        <tbody>
          {tasks.map(task => (
            <tr key={task.id}>
              <td>{task.id}</td>
              <td>{task.task}</td>
              <td>{String(task.isCompleted)}</td>
              <td><button onClick={() => todoHandler(task.id)}>Mark Complete ({task.id})</button></td>
            </tr>
          ))}
        </tbody>

      </table>
    </div>

  );
}




function App() {

  return <TodoProvider />
}
export default App;


代码和框链接:

Edit nervous-tdd-y2y43

答案 1 :(得分:0)

您是否已使用提供程序包装了应用程序?

示例:

import { TodoProvider } from "./src/context/todo_ctx.js";

const App = createAppContainer(navigator);
export default () => {
  return (
    <TodoProvider>
      <App />
    </TodoProvider>
  );
};

答案 2 :(得分:0)

因此,我认为您不需要那里的上下文,尤其是您使用它的方式。我为您尝试过,我知道这是否有帮助:

import React, { useState } from "react";

const Todos = () => {
  const [tasks, setTodos] = useState([
    {
      id: 1,
      task: "Learn React",
      description: "Build a todo app in React",
      isCompleted: false
    },
    {
      id: 2,
      task: "Learn Node",
      description: "Build a Node API",
      isCompleted: false
    },
    {
      id: 3,
      task: "Learn Flutter",
      description: "Complete Flutter Course",
      isCompleted: false
    }
  ]);

  const setTodoHelper = taskId => {
    setTodos(
      tasks.map(x =>
        x.id === taskId ? { ...x, isCompleted: !x.isCompleted } : x
      )
    );
  };

  return (
    <>
      {tasks.map(task => (
        <Todo key={task.id} {...task} setTodoHelper={setTodoHelper} />
      ))}
    </>
  );
};

const Todo = ({ id, setTodoHelper, isCompleted }) => {
  return (
    <>
      <div>{id}</div>
      <div>completed: {String(isCompleted)} </div>
      <button onClick={() => setTodoHelper(id)}>Mark Complete</button>
    </>
  );
};

export default Todos;

ref:https://codesandbox.io/s/cranky-pond-x96sy