为什么“useState”钩子在是函数引用时调用初始状态?

IT技术 javascript reactjs react-hooks
2021-04-25 20:32:00

React 有一个名为useState的钩子,它在向功能组件添加状态时使用。

挂钩API参考状态:

使用状态

const [state, setState] = useState(initialState);

返回一个有状态的值和一个更新它的函数。

在初始渲染期间,返回的状态 ( state) 与作为第一个参数 ( initialState)传递的值相同

setState函数用于更新状态。它接受一个新的状态值并将组件的重新渲染排入队列。

阵营文档状态:

我们传递什么useState作为参数?

useState()Hook的唯一参数是初始状态。与类不同,状态不必是一个对象。如果这就是我们所需要的,我们可以保留一个数字或一个字符串。在我们的例子中,我们只想要一个用户点击次数的数字,所以0作为我们变量的初始状态传递(如果我们想在 state 中存储两个不同的值,我们会调用useState()两次。)

意外行为:

但是,我注意到一些奇怪的、看似没有记录的行为。

如果我尝试使用useState钩子将函数存储为状态,react 将调用函数引用例如

const arbitraryFunction = () => {
    console.log("I have been invoked!");
    return 100;
};

const MyComponent = () => {

    // Trying to store a string - works as expected:
    const [website, setWebsite] = useState("stackoverflow"); // Stores the string
    console.log(typeof website);                             // Prints "string"
    console.log(website);                                    // Prints "stackoverflow"

    // Trying to store a function - doesn't work as expected:
    const [fn, setFn] = useState(arbitraryFunction);         // Prints "I have been invoked!"
    console.log(typeof fn);                                  // Prints "number" (expecting "function")
    console.log(fn);                                         // Prints "100"

    return null; // Don't need to render anything for this example...
};

当我们调用时useState(arbitraryFunction),react 会调用arbitraryFunction并使用它的返回值作为状态。

作为解决方法:

我们可以通过将我们的函数引用包装在另一个函数中来将函数存储为状态。例如

const [fn, setFn] = useState(() => arbitraryFunction)

我还没有遇到任何将函数存储为状态的现实原因,但有人明确选择以不同方式处理函数参数似乎很奇怪。

这个选择可以在整个React 代码库的多个地方看到

initialState = typeof initialArg === 'function' ? initialArg() : initialArg;

为什么这个看似无证的功能存在?

我想不出任何人想要/期望调用他们的函数引用的任何充分理由,但也许你可以。

如果记录在案,则记录在何处?

1个回答

这是记录在这里

惰性初始状态

initialState 参数是初始渲染期间使用的状态。在随后的渲染中,它被忽略。如果初始状态是昂贵计算的结果,您可以提供一个函数,该函数将仅在初始渲染时执行:

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

传递回调setState 也调用回调,但出于不同的原因

功能更新

如果使用先前的状态计算新状态,则可以将函数传递给 setState。该函数将接收先前的值,并返回一个更新的值。这是一个使用两种形式的 setState 的计数器组件示例:

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}