LocalStorage.setItem not called on initial state setting; called with previous value on subsequent form submissions

huangapple go评论72阅读模式
英文:

LocalStorage.setItem not called on initial state setting; called with previous value on subsequent form submissions

问题

我正在做一个简单的待办事项列表来开始学习React。我有以下文件。

我遇到的问题是在表单提交回调handleSubmit期间。第一次输入新的待办事项,比如值为hellotodos被更新并且视图重新绘制,但是localStorage没有更新,而且输入框没有被setTodoEntry清除。

在随后的表单提交中,比如值为worldtodos被更新并且视图重新绘制,但是这次localStorage被更新为上一次调用的值(todoEntry === 'hello')。输入框仍然没有被setTodoEntry清除。在随后的表单提交中,localStorage总是落后一步,输入框永远不会清除。

import { useState } from 'react'
import './App.css'
import Todo from './components/todo'
import { TextField, Button } from '@mui/material';
import { nanoid } from 'nanoid';

function App() {
  let myStoredTodos = localStorage.getItem('todos');
  const [todos, setTodos] = useState(myStoredTodos ? JSON.parse(myStoredTodos) : []); 
  const [todoEntry, setTodoEntry] = useState('');

  function handleTodoEntry(event) {
    setTodoEntry(event.target.value);
  }

  function handleSubmit(event) {
    event.preventDefault();
    if (!todoEntry) return;
    setTodos([ { id: nanoid(), checked: false, value: todoEntry }, ...todos ])
    localStorage.setItem('todos', JSON.stringify(todos));
    setTodoEntry('');
  }

  return (
    <>
      <h1>{`//TODO`}</h1>
      <form onSubmit={handleSubmit}>
        <TextField variant="standard" onChange={(event) => handleTodoEntry(event)} />
        <Button id="add-button" type="submit" variant="contained" disabled={!todoEntry.length}>Add</Button>
      </form>
      
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <Todo checked={todo.checked} value={todo.value} />
          </li>
        ))}
      </ul>
    </>
  )
}

export default App;
英文:

I am doing a simple todo list to start learning React. I have the following file.

The issue I'm running into is during the form submission callback handleSubmit. The first time I enter a new todo, lets say with the value hello, todos is updated and the view redraws, however, localStorage isn't updated and the input isnt cleared by the call to setTodoEntry.

On a subsequent form submission, say with the value world, todos is updated and the view redraws, however this time localStorage is updated with the value from the previous call (todoEntry === &#39;hello&#39;). The input still isnt cleared by setTodoEntry. On subsequent form submissions localStorage is always one "step" behind, and the input never clears.

import { useState } from &#39;react&#39;
import &#39;./App.css&#39;
import Todo from &#39;./components/todo&#39;
import { TextField, Button } from &#39;@mui/material&#39;;
import { nanoid } from &#39;nanoid&#39;;
function App() {
let myStoredTodos = localStorage.getItem(&#39;todos&#39;);
const [todos, setTodos] = useState(myStoredTodos ? JSON.parse(myStoredTodos) : []); 
const [todoEntry, setTodoEntry] = useState(&#39;&#39;);
function handleTodoEntry(event) {
setTodoEntry(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
if (!todoEntry) return;
setTodos([ { id: nanoid(), checked: false, value: todoEntry }, ...todos ])
localStorage.setItem(&#39;todos&#39;, JSON.stringify(todos));
setTodoEntry(&#39;&#39;);
}
return (
&lt;&gt;
&lt;h1&gt;{`//TODO`}&lt;/h1&gt;
&lt;form onSubmit={handleSubmit}&gt;
&lt;TextField variant=&quot;standard&quot; onChange={(event) =&gt; handleTodoEntry(event)} /&gt;
&lt;Button id=&quot;add-button&quot; type=&quot;submit&quot; variant=&quot;contained&quot; disabled={!todoEntry.length}&gt;Add&lt;/Button&gt;
&lt;/form&gt;
&lt;ul&gt;
{todos.map((todo) =&gt; (
&lt;li key={todo.id}&gt;
&lt;Todo checked={todo.checked} value={todo.value} /&gt;
&lt;/li&gt;
))}
&lt;/ul&gt;
&lt;/&gt;
)
}
export default App;

答案1

得分: 1

React状态更新是异步的,todos的值在闭包内基本上是固定的。您可以在effect内的下一次渲染后获取新值。

useEffect(() => {
  localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);

此外,为了保持TextField受控,请将其值设置为todoEntry。(请参阅MUI Text Field文档中的"Uncontrolled vs Controlled"部分。)

<TextField variant="standard" value={todoEntry} onChange={(event) => handleTodoEntry(event)} />
英文:

React state updates are asynchronous and the value of todos is essentially fixed inside the closure. You can instead get the new value after the next render inside an effect.

useEffect(() =&gt; {
	localStorage.setItem(&#39;todos&#39;, JSON.stringify(todos));
}, [todos]);

Also, to keep the TextField controlled, set its value to todoEntry. (See the Uncontrolled vs Controlled section of the MUI Text Field documentation.)

&lt;TextField variant=&quot;standard&quot; value={todoEntry} onChange={(event) =&gt; handleTodoEntry(event)} /&gt;

答案2

得分: 0

setTodos 将安排更新,但不会立即应用它。当您在 setTodos 之后调用 localStorage.setItem('todos', JSON.stringify(todos)); 时,todos 数组尚未更新。

在 SetTodos 函数中使用先前的状态:

function handleSubmit(event) {
  event.preventDefault();
  if (!todoEntry) return;
  setTodos((prevTodos) => {
    const newTodos = [{ id: nanoid(), checked: false, value: todoEntry }, ...prevTodos];
    localStorage.setItem('todos', JSON.stringify(newTodos));
    return newTodos;
  });
  setTodoEntry('');
}

如果输入没有清除,请确保将 todoEntry 状态绑定到 TextField 的值:

<TextField variant="standard" value={todoEntry} onChange={(event) => handleTodoEntry(event)} />

英文:

setTodos will schedule the update but doesn't immediately apply it. When you call localStorage.setItem(&#39;todos&#39;, JSON.stringify(todos)); after setTodos, the todos array hasn't been updated yet.

Use the previous state in the SetTodos function:

function handleSubmit(event) {
event.preventDefault();
if (!todoEntry) return;
setTodos((prevTodos) =&gt; {
const newTodos = [{ id: nanoid(), checked: false, value: todoEntry }, ...prevTodos];
localStorage.setItem(&#39;todos&#39;, JSON.stringify(newTodos));
return newTodos;
});
setTodoEntry(&#39;&#39;);
}

If the input is not cleared, make sure you bind the todoEntry state to the TextField's value:

&lt;TextField variant=&quot;standard&quot; value={todoEntry} onChange={(event) =&gt; handleTodoEntry(event)} /&gt;

huangapple
  • 本文由 发表于 2023年7月20日 09:07:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/76726065.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定