Javascript React input在第一次单击时保存输入,然后在第二次单击时将其显示在UI中。。。为什么?如何修复它

Javascript React input在第一次单击时保存输入,然后在第二次单击时将其显示在UI中。。。为什么?如何修复它,javascript,reactjs,object,input,rendering,Javascript,Reactjs,Object,Input,Rendering,我正在用react制作一个todo列表应用程序,当您键入一个输入,然后单击按钮创建一个新的todoItem对象时,我遇到了这种错误。它显示一个空对象,然后将该输入保存在生产代码内的todoList对象变量中,然后在用户界面中的第二次单击时,第一个todo元素出现了,然后在第三次单击时出现第二个待办事项、第四次单击第三个元素等等。。。我不知道为什么会发生这种情况,我想解决这个问题,下面是我的代码人员: ------------应用程序组件------- 从“React”导入React,{useS

我正在用react制作一个todo列表应用程序,当您键入一个输入,然后单击按钮创建一个新的todoItem对象时,我遇到了这种错误。它显示一个空对象,然后将该输入保存在生产代码内的todoList对象变量中,然后在用户界面中的第二次单击时,第一个todo元素出现了,然后在第三次单击时出现第二个待办事项、第四次单击第三个元素等等。。。我不知道为什么会发生这种情况,我想解决这个问题,下面是我的代码人员:

------------应用程序组件-------
从“React”导入React,{useState};
从“/Form”导入表单;
从“/List”导入列表;
函数App(){
const[input,setInput]=useState(“”);
const[todoItem,setTodoItem]=useState({});
const[todoList,setTodoList]=useState([]);
常量addTodoItem=(e)=>{
e、 预防默认值();
input&&setTodoItem({content:input,id:Math.random()*1000});
setTodoList([…todoList,todoItem]);
设置输入(“”);
};
返回(
);
}
导出默认应用程序;
---------表单组件-----------
从“React”导入React;
函数形式({input,setInput,addTodoItem}){
返回(
待办事项
setInput(e.target.value)}
占位符=“需要做什么”
type=“text”
/>
着手
);
}
导出默认表单;
----------列表组件-----------
从“React”导入React,{useState};
函数列表({setTodoList,todoList}){
const[completed,setCompleted]=使用状态(false);
常量deleteItem=(del)=>{
todoList?.map((项目、i、arr)=>{
setTodoList(arr.filter((value)=>value.id!=del));
log(“项>>>”,项,“索引>>>”,i,“数组>>>”,arr);
});
};
常量反向完成=()=>
已完成?设置已完成(false):设置已完成(true);
返回(
{todoList.map((项,i)=>(
{item.content}

deleteItem(item.id)}>Delete {已完成?“未完成”:“已完成”} ))} ); } 导出默认列表;
第一次尝试的内容为空,因为React的
useState
更新。因此,您将不得不等待
todoItem
更改,以便将其放入
todoList
数组中

一种可能的解决方案是使用React的
useffect
并跟踪
todoItem
上的更改。当它发生更改且其
内容
属性不为空时,您可以将
更新为列表

useEffect(() => {
  if (todoItem.content) {
    setTodoList([...todoList, todoItem]);
  }
}, [todoItem]);

const addTodoItem = (e) => {
  e.preventDefault();
  input && setTodoItem({ content: input, id: Math.random() * 1000 });
  setInput("");
};
const addTodoItem = (e) => {
  e.preventDefault();
  const newTodoItem = { content: input, id: Math.random() * 1000 };
  input && setTodoItem(newTodoItem);
  setTodoList([...todoList, newTodoItem]);
  setInput("");
};
此解决方案可能很容易出错,因为每当
todoItem
引用发生更改时,它都会添加到
todoList
,这不是您想要的

我建议的另一个更好的解决方案是在
addTodoItem
处理程序中声明一个新变量,并将其附加到
todoList
数组中,以及调用
setTodoList

useEffect(() => {
  if (todoItem.content) {
    setTodoList([...todoList, todoItem]);
  }
}, [todoItem]);

const addTodoItem = (e) => {
  e.preventDefault();
  input && setTodoItem({ content: input, id: Math.random() * 1000 });
  setInput("");
};
const addTodoItem = (e) => {
  e.preventDefault();
  const newTodoItem = { content: input, id: Math.random() * 1000 };
  input && setTodoItem(newTodoItem);
  setTodoList([...todoList, newTodoItem]);
  setInput("");
};

但有一件事我不明白,为什么你要把一个变量设为newoditem,为什么不直接设为doitem({content:input,id:Math.random()*1000}),有什么不同呢@Kapobajza@MansouriAla我声明变量
newTodoItem
,因为您可以在两个函数中立即使用它的更新值:
setTodoItem
setTodoList
。然而,如果使用
todoItem
变量,则必须等待
setTodoItem
useState
钩子更新其值,然后才能使用它
input&&setTodoItem({content:input,id:Math.random()*1000})
执行此行后,
todoItem
的值仍然是空对象,
{}
。因此,当下一行执行时:
setTodoList([…todoList,todoItem]),它在数组中设置了一个空对象。是的,我现在知道了,我记得useState不会立即执行,所以下一个值将取一个空对象。非常感谢您的解释,我现在明白了