在React中是否可能使用React.useState(() => {})?

116

我能否将一个 函数 用作我的 React 组件的状态?

这里是示例代码:

// typescript 
type OoopsFunction = () => void;

export function App() {
    const [ooops, setOoops] = React.useState<OoopsFunction>(
        () => console.log('default ooops')
    );

    return (
        <div>
            <div onClick={ ooops }>
                Show Ooops
            </div>

            <div onClick={() => {
                setOoops(() => console.log('other ooops'))
            }}>
                change oops
            </div>
        </div>
    )
}

但它没有起作用......在一开始就会调用defaultOoops,当点击change oops时,other oops将立即记录到控制台中,而不是在再次点击Show Ooops之后记录。

为什么?

我能否将函数用作组件的状态?

或者React有其特殊的方式来处理这种函数状态吗?

3个回答

174

使用hooks可以将函数设置为状态,但是由于状态可以使用返回初始状态或更新后状态的函数进行初始化和更新,因此您需要提供一个函数,该函数反过来返回要放入状态中的函数。

const { useState } = React;

function App() {
  const [ooops, setOoops] = useState(() => () => console.log("default ooops"));

  return (
    <div>
      <button onClick={ooops}>Show Ooops</button>

      <button
        onClick={() => {
          setOoops(() => () => console.log("other ooops"));
        }}
      >
        change oops
      </button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>


4
也许过去这些没有被记录下来,但是现在已经有了。请参考惰性初始状态部分,以及上面关于合并更新的黄色框,即使用prevState。我还发现这篇文章很有帮助:https://medium.com/swlh/how-to-store-a-function-with-the-usestate-hook-in-react-8a88dd4eede1。 - Tyler Collier
2
啊哈哈,这让我的一天都变得美好了。谢谢你。 - aantropov

70

简述

可以使用一个函数作为React组件的状态。为了做到这一点,您必须在React.useState使用返回函数的函数:

const [ooops, setOoops] = React.useState<OoopsFunction>(
    () => () => console.log('default ooops')
);

// or

const yourFunction = () => console.log('default ooops');
const [ooops, setOoops] = React.useState<OoopsFunction>(
    () => yourFunction
);

要更新您的函数,您还必须使用返回您的函数的函数:

setOoops(() => () => console.log("other ooops"));

// or

const otherFunction = () => console.log("other ooops");
setOoops(() => otherFunction);

详细答案

React.useState 的注意事项

ReactuseState 带类型的签名为:

function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

这显示,您有两种方法设置状态的初始值:

  1. 将初始值直接提供给状态(React.useState(0) - 初始值为 0
  2. 提供一个返回初始值的函数(React.useState(() => 0) - 初始值也是 0)。

需要注意的是:如果在 React.useState 中提供了一个函数,则该函数将在 React.useState 执行时被执行,并将其返回值存储为初始状态。

如何实际存储函数

问题在于如果您想将一个函数作为状态存储,您不能像直接提供初始值那样提供它,因为这会导致该函数被执行并将其返回值存储为状态,而不是将函数本身存储为状态。因此,当您编写以下代码时:

const [ooops, setOoops] = React.useState<OoopsFunction>(
    () => console.log('default ooops')
);

当调用React.useState并存储返回值(在本例中为undefined)时,会立即记录'default ooops'

您可以通过提供返回要存储的函数的高阶函数来避免此问题

const [ooops, setOoops] = React.useState<OoopsFunction>(
    () => () => console.log('default ooops')
);

使用这种方式,在第一次运行 React.useState 时,外部函数将被执行,并且它的返回值将被存储。由于此返回值现在是所需函数,因此该函数将被存储。

有关状态设置函数的说明

状态设置函数(此处为 setOoops)的签名如下:

Dispatch<SetStateAction<S>>

带有

type Dispatch<A> = (value: A) => void;
type SetStateAction<S> = S | ((prevState: S) => S);

就像在React.useState中一样,更新状态的方式可以是使用值或返回值的函数。因此,为了更新状态,还必须使用上面的高阶函数。


1
谢谢你补充这个细节,Peter。对我来说非常有帮助! - Mike T
你如何执行存储在状态中的函数呢?你不能像普通函数一样使用ooops()进行调用,对吧? - ANimator120
@ANimator120 是的,你可以完全按照那样执行它。这也是你可以尝试接受的答案。 - Peter Lehnhardt

1
之前的回答让我找到了正确的方向,但是我需要稍微调整一下我的setState参数,因为我想执行一个带有参数的函数。以下代码对我有效!
const [handler, setHandler] = useState(() => {});

setHandler(() => () => enableScheduleById(scheduleId));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接