useState 是一个 React Hook,它允许你向你的组件添加一个 状态变量。
const [state, setState] = useState(initialState)参考
🌐 Reference
useState(initialState)
在组件的顶层调用 useState 来声明一个状态变量。
🌐 Call useState at the top level of your component to declare a state variable.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(28);
const [name, setName] = useState('Taylor');
const [todos, setTodos] = useState(() => createTodos());
// ...惯例是使用数组解构来命名状态变量,如 [something, setSomething]。
🌐 The convention is to name state variables like [something, setSomething] using array destructuring.
参数
🌐 Parameters
initialState:你希望状态最初的值。它可以是任何类型的值,但函数有特殊的行为。此参数在初次渲染后会被忽略。- 如果你将一个函数作为
initialState传入,它将被视为一个 初始化函数。它应该是纯函数,不应接受任何参数,并且应该返回任何类型的值。React 会在初始化组件时调用你的初始化函数,并将其返回值存储为初始状态。请参见下面的示例。
- 如果你将一个函数作为
返回
🌐 Returns
useState 返回一个包含恰好两个值的数组:
- 当前状态。在第一次渲染时,它将匹配你传递的
initialState。 set函数 允许你将状态更新为不同的值并触发重新渲染。
注意事项
🌐 Caveats
useState是一个 Hook,因此你只能在你的组件的顶层或你自己的 Hook 中调用它。你不能在循环或条件语句中调用它。如果你需要那样做,可以提取一个新组件并将状态移入其中。- 在严格模式下,React 会调用你的初始化函数两次,以便帮助你发现意外的副作用。这是仅在开发环境中的行为,不会影响生产环境。如果你的初始化函数是纯函数(正如应该的那样),这不会影响行为。两次调用中的一个结果将被忽略。
set 的功能,类似于 setSomething(nextState)
🌐 set functions, like setSomething(nextState)
useState 返回的 set 函数允许你将状态更新为不同的值并触发重新渲染。你可以直接传入下一个状态,或传入一个根据上一个状态计算它的函数:
🌐 The set function returned by useState lets you update the state to a different value and trigger a re-render. You can pass the next state directly, or a function that calculates it from the previous state:
const [name, setName] = useState('Edward');
function handleClick() {
setName('Taylor');
setAge(a => a + 1);
// ...参数
🌐 Parameters
nextState:你希望状态具有的值。它可以是任何类型的值,但函数有一个特殊的行为。- 如果你将一个函数作为
nextState传入,它将被视为一个 更新函数。它必须是纯函数,应该以待处理状态作为唯一参数,并返回下一个状态。React 会将你的更新函数放入队列并重新渲染你的组件。在下一次渲染期间,React 将通过将所有排队的更新函数应用到先前的状态来计算下一个状态。请参见下面的示例。
- 如果你将一个函数作为
返回
🌐 Returns
set 函数没有返回值。
注意事项
🌐 Caveats
set函数仅会更新 下一次 渲染的状态变量。如果在调用set函数后读取状态变量,你仍然会得到调用前屏幕上显示的旧值。- 如果你提供的新值与当前的
state相同(通过Object.is比较确定),React 将跳过重新渲染该组件及其子组件。 这是一个优化。尽管在某些情况下,React 仍可能在跳过子组件之前调用你的组件,但这不应该影响你的代码。 - React 批量处理状态更新。 它会在所有事件处理程序运行完毕并调用它们的
set函数后更新屏幕。这可以防止在单个事件中发生多次重新渲染。在极少数情况下,如果你需要强制 React 提前更新屏幕,例如访问 DOM,你可以使用flushSync. set函数具有稳定的标识,因此你经常会看到它在 Effect 依赖中被省略,但包括它不会导致 Effect 被触发。如果 linter 允许你在没有错误的情况下省略依赖,那么这样做是安全的。了解更多关于移除 Effect 依赖的信息。- 在 渲染期间 调用
set函数仅允许在当前正在渲染的组件中进行。React 会丢弃它的输出,并立即尝试使用新的状态再次渲染它。这个模式很少需要,但你可以用它来 存储来自之前渲染的信息。参见下面的示例。 - 在严格模式下,React 会调用你的更新函数两次,以便帮助你发现意外的副作用。这是仅在开发环境中的行为,不会影响生产环境。如果你的更新函数是纯函数(正如应该的那样),这不会影响行为。两次调用中的一个结果将被忽略。
用法
🌐 Usage
向组件添加状态
🌐 Adding state to a component
在组件的顶层调用 useState 来声明一个或多个状态变量。
🌐 Call useState at the top level of your component to declare one or more state variables.
import { useState } from 'react';
function MyComponent() {
const [age, setAge] = useState(42);
const [name, setName] = useState('Taylor');
// ...惯例是使用数组解构来命名状态变量,如 [something, setSomething]。
🌐 The convention is to name state variables like [something, setSomething] using array destructuring.
useState 返回一个恰好包含两个元素的数组:
- 这个状态变量的 当前状态 ,最初设置为你提供的 初始状态 。
- The
set函数 允许你根据交互将其更改为任何其他值。
要更新屏幕上的内容,请使用一些下一个状态调用 set 函数:
🌐 To update what’s on the screen, call the set function with some next state:
function handleClick() {
setName('Robin');
}React 将存储下一个状态,使用新值再次渲染你的组件,并更新 UI。
🌐 React will store the next state, render your component again with the new values, and update the UI.
例子 1 of 4: 计数器(数字)
🌐 Counter (number)
在这个例子中,count 状态变量保存一个数字。点击按钮会使其增加。
🌐 In this example, the count state variable holds a number. Clicking the button increments it.
import { useState } from 'react'; export default function Counter() { const [count, setCount] = useState(0); function handleClick() { setCount(count + 1); } return ( <button onClick={handleClick}> You pressed me {count} times </button> ); }
根据之前的状态更新状态
🌐 Updating state based on the previous state
假设 age 是 42。这个处理程序调用 setAge(age + 1) 三次:
🌐 Suppose the age is 42. This handler calls setAge(age + 1) three times:
function handleClick() {
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
setAge(age + 1); // setAge(42 + 1)
}然而,点击一次后,age 将只会变成 43 而不是 45!这是因为调用 set 函数 不会更新 已经运行代码中的 age 状态变量。所以每次 setAge(age + 1) 调用都变成了 setAge(43)。
🌐 However, after one click, age will only be 43 rather than 45! This is because calling the set function does not update the age state variable in the already running code. So each setAge(age + 1) call becomes setAge(43).
为了解决这个问题,你可以将一个更新函数传递给 setAge,而不是传递下一个状态:
🌐 To solve this problem, you may pass an updater function to setAge instead of the next state:
function handleClick() {
setAge(a => a + 1); // setAge(42 => 43)
setAge(a => a + 1); // setAge(43 => 44)
setAge(a => a + 1); // setAge(44 => 45)
}这里,a => a + 1 是你的更新函数。它接受 待处理状态 并从中计算出 下一个状态 。
React 会将你的更新函数放入一个队列。然后,在下一次渲染时,它将按相同的顺序调用它们:
🌐 React puts your updater functions in a queue. Then, during the next render, it will call them in the same order:
a => a + 1将接收42作为待处理状态,并返回43作为下一个状态。a => a + 1将接收43作为待处理状态,并返回44作为下一个状态。a => a + 1将接收44作为待处理状态,并返回45作为下一个状态。
没有其他排队的更新,因此 React 最终会将 45 存储为当前状态。
🌐 There are no other queued updates, so React will store 45 as the current state in the end.
按照惯例,通常将挂起状态的参数命名为状态变量名称的首字母,比如将 age 的挂起状态命名为 a。但是,你也可以将其命名为 prevAge 或者其他你认为更清晰的名称。
🌐 By convention, it’s common to name the pending state argument for the first letter of the state variable name, like a for age. However, you may also call it like prevAge or something else that you find clearer.
在开发环境中,React 可能会调用你的更新器两次以验证它们是否是纯的。
🌐 React may call your updaters twice in development to verify that they are pure.
深入研究
🌐 Is using an updater always preferred?
你可能会听到有人建议,如果你设置的状态是根据之前的状态计算得出的,那么应该总是像 setAge(a => a + 1) 那样编写代码。这种做法没有坏处,但也并非总是必要的。
🌐 You might hear a recommendation to always write code like setAge(a => a + 1) if the state you’re setting is calculated from the previous state. There is no harm in it, but it is also not always necessary.
在大多数情况下,这两种方法之间没有区别。React 总是确保,对于用户的有意操作,比如点击,age 状态变量会在下次点击之前更新。这意味着点击处理函数在事件处理函数开始时看到过时的 age 的风险不存在。
🌐 In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the age state variable would be updated before the next click. This means there is no risk of a click handler seeing a “stale” age at the beginning of the event handler.
然而,如果你在同一个事件中进行多次更新,更新函数可能会很有用。如果直接访问状态变量本身不方便(在优化重新渲染时可能会遇到这种情况),它们也很有用。
🌐 However, if you do multiple updates within the same event, updaters can be helpful. They’re also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).
如果你更喜欢一致性而不是稍微冗长的语法,那么如果你设置的状态是根据先前的状态计算的,总是编写一个更新函数是合理的。如果它是根据某个其他状态变量的先前状态计算的,你可能想把它们合并成一个对象并使用 reducer.
🌐 If you prefer consistency over slightly more verbose syntax, it’s reasonable to always write an updater if the state you’re setting is calculated from the previous state. If it’s calculated from the previous state of some other state variable, you might want to combine them into one object and use a reducer.
例子 1 of 2: 传递更新函数
🌐 Passing the updater function
这个例子传递了更新函数,所以“+3”按钮可以使用。
🌐 This example passes the updater function, so the “+3” button works.
import { useState } from 'react'; export default function Counter() { const [age, setAge] = useState(42); function increment() { setAge(a => a + 1); } return ( <> <h1>Your age: {age}</h1> <button onClick={() => { increment(); increment(); increment(); }}>+3</button> <button onClick={() => { increment(); }}>+1</button> </> ); }
更新状态中的对象和数组
🌐 Updating objects and arrays in state
你可以将对象和数组放入 state。在 React 中,state 被认为是只读的,所以 你应该替换它,而不是修改你现有的对象。例如,如果你在 state 中有一个 form 对象,不要修改它:
🌐 You can put objects and arrays into state. In React, state is considered read-only, so you should replace it rather than mutate your existing objects. For example, if you have a form object in state, don’t mutate it:
// 🚩 Don't mutate an object in state like this:
form.firstName = 'Taylor';而是,通过创建一个新对象来替换整个对象:
🌐 Instead, replace the whole object by creating a new one:
// ✅ Replace state with a new object
setForm({
...form,
firstName: 'Taylor'
});🌐 Read updating objects in state and updating arrays in state to learn more.
例子 1 of 4: 表单(对象)
🌐 Form (object)
在这个例子中,form 状态变量保存一个对象。每个输入都有一个变更处理函数,该函数调用 setForm 并传入整个表单的下一个状态。{ ...form } 展开语法确保状态对象被替换而不是被修改。
🌐 In this example, the form state variable holds an object. Each input has a change handler that calls setForm with the next state of the entire form. The { ...form } spread syntax ensures that the state object is replaced rather than mutated.
import { useState } from 'react'; export default function Form() { const [form, setForm] = useState({ firstName: 'Barbara', lastName: 'Hepworth', email: 'bhepworth@sculpture.com', }); return ( <> <label> First name: <input value={form.firstName} onChange={e => { setForm({ ...form, firstName: e.target.value }); }} /> </label> <label> Last name: <input value={form.lastName} onChange={e => { setForm({ ...form, lastName: e.target.value }); }} /> </label> <label> Email: <input value={form.email} onChange={e => { setForm({ ...form, email: e.target.value }); }} /> </label> <p> {form.firstName}{' '} {form.lastName}{' '} ({form.email}) </p> </> ); }
避免重新创建初始状态
🌐 Avoiding recreating the initial state
React 会保存一次初始状态,并在下一次渲染时忽略它。
🌐 React saves the initial state once and ignores it on the next renders.
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos());
// ...尽管 createInitialTodos() 的结果仅用于初始渲染,但你仍然在每次渲染时调用这个函数。如果它创建了大型数组或执行昂贵的计算,这可能会浪费资源。
🌐 Although the result of createInitialTodos() is only used for the initial render, you’re still calling this function on every render. This can be wasteful if it’s creating large arrays or performing expensive calculations.
为了解决这个问题,你可以将其作为 initializer 函数传递给 useState,如下所示:
🌐 To solve this, you may pass it as an initializer function to useState instead:
function TodoList() {
const [todos, setTodos] = useState(createInitialTodos);
// ...注意,你传递的是 createInitialTodos,它是函数本身,而不是 createInitialTodos(),它是调用函数的结果。如果你将一个函数传递给 useState,React 只会在初始化时调用它。
🌐 Notice that you’re passing createInitialTodos, which is the function itself, and not createInitialTodos(), which is the result of calling it. If you pass a function to useState, React will only call it during initialization.
在开发中,React 可能会调用你的初始化器两次以验证它们是纯粹的。
🌐 React may call your initializers twice in development to verify that they are pure.
例子 1 of 2: 传递初始化函数
🌐 Passing the initializer function
这个示例传递了初始化函数,因此 createInitialTodos 函数只在初始化时运行。它在组件重新渲染时不会运行,例如当你在输入框中输入内容时。
🌐 This example passes the initializer function, so the createInitialTodos function only runs during initialization. It does not run when component re-renders, such as when you type into the input.
import { useState } from 'react'; function createInitialTodos() { const initialTodos = []; for (let i = 0; i < 50; i++) { initialTodos.push({ id: i, text: 'Item ' + (i + 1) }); } return initialTodos; } export default function TodoList() { const [todos, setTodos] = useState(createInitialTodos); const [text, setText] = useState(''); return ( <> <input value={text} onChange={e => setText(e.target.value)} /> <button onClick={() => { setText(''); setTodos([{ id: todos.length, text: text }, ...todos]); }}>Add</button> <ul> {todos.map(item => ( <li key={item.id}> {item.text} </li> ))} </ul> </> ); }
使用键来重置状态
🌐 Resetting state with a key
在渲染列表时,你经常会遇到 key 属性。然而,它还有另一种用途。
🌐 You’ll often encounter the key attribute when rendering lists. However, it also serves another purpose.
你可以通过向组件传递不同的 key 来重置组件的状态。 在这个例子中,重置按钮会改变 version 状态变量,我们将其作为 key 传递给 Form。当 key 改变时,React 会从头重新创建 Form 组件(及其所有子组件),因此其状态会被重置。
🌐 You can reset a component’s state by passing a different key to a component. In this example, the Reset button changes the version state variable, which we pass as a key to the Form. When the key changes, React re-creates the Form component (and all of its children) from scratch, so its state gets reset.
阅读 保留和重置状态 以了解更多信息。
🌐 Read preserving and resetting state to learn more.
import { useState } from 'react'; export default function App() { const [version, setVersion] = useState(0); function handleReset() { setVersion(version + 1); } return ( <> <button onClick={handleReset}>Reset</button> <Form key={version} /> </> ); } function Form() { const [name, setName] = useState('Taylor'); return ( <> <input value={name} onChange={e => setName(e.target.value)} /> <p>Hello, {name}.</p> </> ); }
存储以前渲染的信息
🌐 Storing information from previous renders
通常,你会在事件处理程序中更新状态。然而,在少数情况下,你可能希望在渲染时调整状态——例如,当一个 prop 改变时,你可能希望更改一个状态变量。
🌐 Usually, you will update state in event handlers. However, in rare cases you might want to adjust state in response to rendering — for example, you might want to change a state variable when a prop changes.
在大多数情况下,你不需要这样:
🌐 In most cases, you don’t need this:
- 如果你需要的值可以完全由当前的 props 或其他状态计算出来,就完全去掉那个多余的状态。 如果你担心计算过于频繁,
useMemoHook 可以提供帮助。 - 如果你想重置整个组件树的状态,向你的组件传递一个不同的
key。 - 如果可以,请更新事件处理程序中的所有相关状态。
在极少数情况下,如果这些都不适用,你可以使用一种模式,根据到目前为止已经渲染的值来更新状态,通过在组件渲染时调用 set 函数。
🌐 In the rare case that none of these apply, there is a pattern you can use to update state based on the values that have been rendered so far, by calling a set function while your component is rendering.
这是一个例子。这个 CountLabel 组件显示传递给它的 count 属性:
🌐 Here’s an example. This CountLabel component displays the count prop passed to it:
export default function CountLabel({ count }) {
return <h1>{count}</h1>
}假设你想显示计数器自上次更改以来是增加还是减少。count 属性并不能告诉你这一点——你需要跟踪它的先前值。添加 prevCount 状态变量来跟踪它。再添加另一个名为 trend 的状态变量,用于保存计数是增加还是减少。比较 prevCount 与 count,如果它们不相等,更新 prevCount 和 trend。现在你可以显示当前的计数属性以及它自上次渲染以来的变化。
🌐 Say you want to show whether the counter has increased or decreased since the last change. The count prop doesn’t tell you this — you need to keep track of its previous value. Add the prevCount state variable to track it. Add another state variable called trend to hold whether the count has increased or decreased. Compare prevCount with count, and if they’re not equal, update both prevCount and trend. Now you can show both the current count prop and how it has changed since the last render.
import { useState } from 'react'; export default function CountLabel({ count }) { const [prevCount, setPrevCount] = useState(count); const [trend, setTrend] = useState(null); if (prevCount !== count) { setPrevCount(count); setTrend(count > prevCount ? 'increasing' : 'decreasing'); } return ( <> <h1>{count}</h1> {trend && <p>The count is {trend}</p>} </> ); }
请注意,如果在渲染时调用 set 函数,它必须位于类似 prevCount !== count 的条件中,并且条件内部必须有类似 setPrevCount(count) 的调用。否则,你的组件将会循环重新渲染,直到崩溃。另外,你只能以这种方式更新当前正在渲染组件的状态。在渲染期间调用另一个组件的 set 函数是错误的。最后,你的 set 调用仍应在不变更的情况下更新状态——这并不意味着你可以违反纯函数的其他规则。
🌐 Note that if you call a set function while rendering, it must be inside a condition like prevCount !== count, and there must be a call like setPrevCount(count) inside of the condition. Otherwise, your component would re-render in a loop until it crashes. Also, you can only update the state of the currently rendering component like this. Calling the set function of another component during rendering is an error. Finally, your set call should still update state without mutation — this doesn’t mean you can break other rules of pure functions.
这个模式可能很难理解,通常最好避免使用。然而,它仍然比在 effect 中更新状态更好。当你在渲染期间调用 set 函数时,React 会在你的组件以 return 语句退出后立即重新渲染该组件,并且在渲染子组件之前执行。这样,子组件就不需要渲染两次。你组件函数的其余部分仍会执行(结果会被丢弃)。如果你的条件在所有 Hook 调用之后,你可以添加一个早期的 return; 来更早地重新开始渲染。
🌐 This pattern can be hard to understand and is usually best avoided. However, it’s better than updating state in an effect. When you call the set function during render, React will re-render that component immediately after your component exits with a return statement, and before rendering the children. This way, children don’t need to render twice. The rest of your component function will still execute (and the result will be thrown away). If your condition is below all the Hook calls, you may add an early return; to restart rendering earlier.
故障排除
🌐 Troubleshooting
我更新了状态,但日志记录给了我旧值
🌐 I’ve updated the state, but logging gives me the old value
调用 set 函数不会改变运行代码中的状态:
🌐 Calling the set function does not change state in the running code:
function handleClick() {
console.log(count); // 0
setCount(count + 1); // Request a re-render with 1
console.log(count); // Still 0!
setTimeout(() => {
console.log(count); // Also 0!
}, 5000);
}这是因为状态表现得像一个快照。 更新状态会请求使用新的状态值重新渲染,但不会影响你在已运行事件处理程序中的 count JavaScript 变量。
🌐 This is because states behaves like a snapshot. Updating state requests another render with the new state value, but does not affect the count JavaScript variable in your already-running event handler.
如果你需要使用下一个状态,可以在将其传递给 set 函数之前将其保存在变量中:
🌐 If you need to use the next state, you can save it in a variable before passing it to the set function:
const nextCount = count + 1;
setCount(nextCount);
console.log(count); // 0
console.log(nextCount); // 1我已经更新了状态,但是屏幕没有更新
🌐 I’ve updated the state, but the screen doesn’t update
如果下一个状态与先前状态相等,React 会**忽略你的更新,**这一点是通过 Object.is 比较确定的。这通常发生在你直接更改状态中的对象或数组时:
🌐 React will ignore your update if the next state is equal to the previous state, as determined by an Object.is comparison. This usually happens when you change an object or an array in state directly:
obj.x = 10; // 🚩 Wrong: mutating existing object
setObj(obj); // 🚩 Doesn't do anything你修改了一个已有的 obj 对象并将其传回 setObj,所以 React 忽略了更新。要解决这个问题,你需要确保始终在状态中_替换_对象和数组,而不是_修改_它们:
🌐 You mutated an existing obj object and passed it back to setObj, so React ignored the update. To fix this, you need to ensure that you’re always replacing objects and arrays in state instead of mutating them:
// ✅ Correct: creating a new object
setObj({
...obj,
x: 10
});我收到一个错误:“重新渲染次数过多”
🌐 I’m getting an error: “Too many re-renders”
你可能会收到一个错误提示:Too many re-renders. React limits the number of renders to prevent an infinite loop.。通常,这意味着你在 渲染期间 无条件地设置状态,所以你的组件进入了一个循环:渲染,设置状态(这会导致再次渲染),渲染,设置状态(这会导致再次渲染),以此类推。通常,这种情况是由于在指定事件处理程序时的错误导致的:
🌐 You might get an error that says: Too many re-renders. React limits the number of renders to prevent an infinite loop. Typically, this means that you’re unconditionally setting state during render, so your component enters a loop: render, set state (which causes a render), render, set state (which causes a render), and so on. Very often, this is caused by a mistake in specifying an event handler:
// 🚩 Wrong: calls the handler during render
return <button onClick={handleClick()}>Click me</button>
// ✅ Correct: passes down the event handler
return <button onClick={handleClick}>Click me</button>
// ✅ Correct: passes down an inline function
return <button onClick={(e) => handleClick(e)}>Click me</button>如果你找不到此错误的原因,请点击控制台中错误旁边的箭头,并查看 JavaScript 堆栈,以找出导致错误的具体 set 函数调用。
🌐 If you can’t find the cause of this error, click on the arrow next to the error in the console and look through the JavaScript stack to find the specific set function call responsible for the error.
我的初始化或更新函数运行两次
🌐 My initializer or updater function runs twice
在 严格模式 下,React 会调用你的一些函数两次,而不是一次:
🌐 In Strict Mode, React will call some of your functions twice instead of once:
function TodoList() {
// This component function will run twice for every render.
const [todos, setTodos] = useState(() => {
// This initializer function will run twice during initialization.
return createTodos();
});
function handleClick() {
setTodos(prevTodos => {
// This updater function will run twice for every click.
return [...prevTodos, createTodo()];
});
}
// ...这是预期的,不应破坏你的代码。
🌐 This is expected and shouldn’t break your code.
这种 仅限开发环境 的行为可以帮助你 保持组件的纯粹性。React 会使用其中一次调用的结果,并忽略另一次调用的结果。只要你的组件、初始化器和更新器函数是纯函数,这不会影响你的逻辑。然而,如果它们不小心变得不纯,这可以帮助你发现错误。
🌐 This development-only behavior helps you keep components pure. React uses the result of one of the calls, and ignores the result of the other call. As long as your component, initializer, and updater functions are pure, this shouldn’t affect your logic. However, if they are accidentally impure, this helps you notice the mistakes.
例如,这个不纯的更新函数改变了状态数组:
🌐 For example, this impure updater function mutates an array in state:
setTodos(prevTodos => {
// 🚩 Mistake: mutating state
prevTodos.push(createTodo());
});因为 React 会调用你的更新函数两次,你会看到待办事项被添加了两次,所以你就会知道有一个错误。在这个例子中,你可以通过替换数组而不是修改它来修复这个错误:
🌐 Because React calls your updater function twice, you’ll see the todo was added twice, so you’ll know that there is a mistake. In this example, you can fix the mistake by replacing the array instead of mutating it:
setTodos(prevTodos => {
// ✅ Correct: replacing with new state
return [...prevTodos, createTodo()];
});既然这个更新函数是纯函数,额外调用一次并不会影响行为。这就是为什么 React 调用它两次可以帮助你发现错误的原因。只有组件、初始化函数和更新函数需要是纯函数。 事件处理函数不需要是纯函数,因此 React 永远不会调用你的事件处理函数两次。
🌐 Now that this updater function is pure, calling it an extra time doesn’t make a difference in behavior. This is why React calling it twice helps you find mistakes. Only component, initializer, and updater functions need to be pure. Event handlers don’t need to be pure, so React will never call your event handlers twice.
阅读 保持组件纯粹 以了解更多信息。
🌐 Read keeping components pure to learn more.
我正在尝试将状态设置为一个函数,但它被调用了
🌐 I’m trying to set state to a function, but it gets called instead
你不能像这样将函数置于状态:
🌐 You can’t put a function into state like this:
const [fn, setFn] = useState(someFunction);
function handleClick() {
setFn(someOtherFunction);
}因为你传递的是一个函数,React 假设 someFunction 是一个 初始化函数,而 someOtherFunction 是一个 更新函数,所以它会尝试调用它们并存储结果。要真正存储一个函数,你必须在两种情况下都在它们前面加上 () =>。然后 React 就会存储你传递的函数。
🌐 Because you’re passing a function, React assumes that someFunction is an initializer function, and that someOtherFunction is an updater function, so it tries to call them and store the result. To actually store a function, you have to put () => before them in both cases. Then React will store the functions you pass.
const [fn, setFn] = useState(() => someFunction);
function handleClick() {
setFn(() => someOtherFunction);
}