英文:
LocalStorage.setItem not called on initial state setting; called with previous value on subsequent form submissions
问题
我正在做一个简单的待办事项列表来开始学习React。我有以下文件。
我遇到的问题是在表单提交回调handleSubmit
期间。第一次输入新的待办事项,比如值为hello
,todos
被更新并且视图重新绘制,但是localStorage没有更新,而且输入框没有被setTodoEntry
清除。
在随后的表单提交中,比如值为world
,todos
被更新并且视图重新绘制,但是这次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 === 'hello'
). 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 '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;
答案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(() => {
localStorage.setItem('todos', 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.)
<TextField variant="standard" value={todoEntry} onChange={(event) => handleTodoEntry(event)} />
答案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('todos', 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) => {
const newTodos = [{ id: nanoid(), checked: false, value: todoEntry }, ...prevTodos];
localStorage.setItem('todos', JSON.stringify(newTodos));
return newTodos;
});
setTodoEntry('');
}
If the input is not cleared, make sure you bind the todoEntry state to the TextField's value:
<TextField variant="standard" value={todoEntry} onChange={(event) => handleTodoEntry(event)} />
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论