简体   繁体   English

单击后无法使用reducer React JS更新值

[英]Can't update value using reducer React JS after click

I'm currently learning useReducer and try to convert useState to useReducer in todo app.我目前正在学习 useReducer 并尝试在 todo 应用程序中将 useState 转换为 useReducer。 My problem :我的问题 :

  1. TOGGLE_TODO can't update the value when click.点击时 TOGGLE_TODO 无法更新值。
// First try
case TOGGLE_TODO:
   let targetId = todoItem[action.index];
   let newTodo = [...todoItem];
   return (newTodo[targetId].completed = !newTodo[targetId].completed);

// Second try
case TOGGLE_TODO:
   return todoItem.map((todo, index) => {
       if (index === action.index) {
          return { ...todo, completed: !todo.completed };
       }
       return todo;
});
<button
    value={index}
    onClick={(event) =>
       dispatch({
          type: TOGGLE_TODO,
          index: event.target.value,
       })
    }
 >
    {todo.completed ? "done" : "pending"}
 </button>
  1. UPDATE_TODO, I have no clue for convert this to useReducer. UPDATE_TODO,我不知道将其转换为 useReducer。 Can I convert this too ?我也可以转换吗? Here is my code using useState.这是我使用 useState 的代码。
const onUpdate = (e) => {
   const target = e.currentTarget.value;
   const todoTarget = todoItem[target].name;
   setInput(todoTarget);
   setTodoIndex(target);
};

And here is my codesandbox for full code.这是我的完整代码的代码框。 MY CODE USING USESTATE and MY CODE USING USEREDUCER 我的代码使用 USESTATE我的代码使用 USEREDUCER

First, you first TOGGLE_TODO handler if flawed -首先,如果有缺陷,您首先要使用 TOGGLE_TODO 处理程序 -

// First try
case TOGGLE_TODO:
   let targetId = todoItem[action.index];
   let newTodo = [...todoItem];
   return (newTodo[targetId].completed = !newTodo[targetId].completed);

Your todoAction is actually a reducer and not an action, so you should rename it to todoReducer .你的todoAction实际上是一个 reducer 而不是一个动作,所以你应该把它重命名为todoReducer Also, a reducer needs to return a new copy of the entire state, and not just change one part of it, so your second try is correct.此外,reducer 需要返回整个状态的新副本,而不仅仅是更改其中的一部分,因此您的第二次尝试是正确的。

case TOGGLE_TODO:
   return todoItem.map((todo, index) => {
       if (index === action.index) {
          return { ...todo, completed: !todo.completed };
       }
       return todo;
});

Notice how in the first case you are returning one todoItem, where in the second case you are returning an entire new array.请注意,在第一种情况下,您将返回一个 todoItem,而在第二种情况下,您将返回一个全新的数组。

The problem with the code is that in your button, when you dispatch the action with the value, you are dispatching a string -代码的问题在于,在您的按钮中,当您发送带有值的操作时,您正在发送一个字符串 -

<button
    value={index}
    onClick={(event) =>
       dispatch({
          type: TOGGLE_TODO,
          index: event.target.value,  // <- This is a string
       })
    }
 >
    {todo.completed ? "done" : "pending"}
 </button>

And in your reducer you are trying to compare it to a number -在你的减速器中,你试图将它与一个数字进行比较 -

if (index === action.index) // This will always be false since index is a .number and action.index is a string

The solution is to dispatch a number like so -解决方案是发送一个这样的号码 -

dispatch({
          type: TOGGLE_TODO,
          index: Number(event.target.value),
       })

working codesandbox - https://codesandbox.io/s/long-monad-5yzjv7?file=/src/App.js工作代码框 - https://codesandbox.io/s/long-monad-5yzjv7?file=/src/App.js

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM