将变量传递给React useCallback挂钩

时间:2019-11-18 15:57:25

标签: reactjs react-hooks

我在学习React钩子时遇到了困难。

我正在此网站https://reactjs.org/docs/hooks-reference.html

上关注
const onGameOver = React.useCallback(
  ({ playerScore, playerHealth, gameId }) => {
    setPages(player =>
      arrayMove(playerScore, playerHealth, gameId)
    );
    console.log('gameId: ', gameId);
  },
  [player, gameId]
);

我可以看到playerScore和playerHealth,但看不到gameId。

我将'gameId'放在我的依赖项数组中,但在console.log中始终是'undefined'。

出于测试目的,我只是给gameId一个虚拟ID,如下所示:

const gameId = useState(123);

但是最终,我会这样使用它:

<GameOverScreen controlId={ControlId} stats={endGameStats} onGameOver=({onGameOver, gameId}) />

我可能做错了什么?

谢谢

3 个答案:

答案 0 :(得分:2)

依赖数组中的gameId在调用时与函数内部的值不同。这是因为您的函数定义从传递给它的第一个参数中解构了gameId

                                                                   vvvvvv
const onGameOver = React.useCallback(({ playerScore, playerHealth, gameId }) => {

这将“遮蔽”传递给gameId的函数的React.useCallback() 外部的值。

传递给React.useCallback()的依赖项数组 not 隐式传递给正在创建的函数。该数组用于确定在特定渲染器上传递给React.useCallback()的函数是否应替换React *记录的函数-记住,React.useCallback()大致等效于:

React.useMemo(() => f, deps)

执行时,您要么必须将gameId传递给onGameOver,像这样:

onGameOver({ gameId: .... })

或者您将需要从解构分配中删除gameId

const onGameOver = React.useCallback(({ playerScore, playerHealth }) => {

后者是可能的正确方法,因为这种方式onGameOver将始终具有gameId的正确值,而无需调用者知道它。


*依赖项数组是必需的,因为在每个渲染器上都会调用钩子本身,但我们可能希望在不同渲染器上保持一些值稳定。

每个渲染中,将deps数组中的每个元素与上一个渲染中的deps数组进行比较。如果其中任何一个已更改,则该钩子将被标记为“陈旧”,并且将根据钩子而发生某种效果:

  • useMemo(f, deps)将执行功能f,该功能的返回值将作为当前当前渲染useMemo()的返回值提供渲染,直到deps再次更改。
  • useCallback()useMemo()的包装,在打算记住一个功能时,使用起来稍微容易一些。 useCallback(f, deps)等同于useMemo(() => f, deps)
  • 当依赖项数组更改时,
  • useEffect(f, deps)useLayoutEffect(f, deps)执行f,尽管何时执行这些功能会有所不同基于您使用的挂钩。如果需要与DOM交互,则应使用useLayoutEffect(),否则,应使用useEffect()

这就是为什么在数组deps处使用空数组会导致效果只在组件生命周期内执行一次的原因-因为数组值永远不会改变,所以效果永远不会重新运行

答案 1 :(得分:1)

根据您的修改进行更新

您添加了const gameId = useState(123);,但这并不完全正确。 useState返回具有状态值的数组,以及可以调用以更新该状态的函数。通常,您应该这样做:

const [gameId, setGameId] = useState(123)

然后您添加了此内容:

<GameOverScreen ... onGameOver=({onGameOver, gameId}) />

但这不是有效的JSX。要在组件上设置道具,您需要将其放在诸如<MyComponent message="Hello"/>之类的引号中,或置于诸如<MyComponent gameId={gameId}/>之类的花括号中。括号不起作用。另外,我不确定您要使用{onGameOver, gameId}之类的值做什么...如果您尝试将这两件事作为道具传递,它应该更像

<GameOverScreen ... onGameOver={onGameOver} gameId={gameId} />

原始答案

想象一下您的功能:

function actualOnGameOver({ playerScore, playerHealth, gameId }) {
  setPages(player =>
    arrayMove(playerScore, playerHealth, gameId)
  );
  console.log('gameId: ', gameId);
}

查看该函数引用的所有事物,并确定它们来自何处:

  • setPages-从函数外部开始(我猜是从useState调用开始)
  • arrayMove-从函数外部开始(我猜是从import开始)
  • playerScore-作为参数收到
  • playerHealth-作为参数收到
  • gameId-作为参数收到

请注意,useCallbackuseMemo的便捷版本,即它正在制作/获取函数的缓存版本。依赖项数组用于告诉React什么时候应该使该缓存无效。在该数组中应提及来自函数外部的,函数引用的任何非常数值。作为函数的参数接收的值(例如gameId)不应放在其中。

因此,您的依赖项数组应为[setPages](如果我对arrayMove是导入的猜测是错误的,则应为[setPages, arrayMove]),因为这是该函数引用的唯一未传递的非恒定值作为参数。

actualOnGameOver传递到useCallback时,结果是具有相同签名的函数,因此您将以相同的方式调用onGameOver,例如

onGameOver({
  playerScore: 100,
  playerHealth: 75,
  gameId: 'abc123'
})

修复了依赖性数组后,如果gameId仍未定义,则应查看onGameOver函数外部的代码。确保在致电onGameOver时传递gameId的值。

答案 2 :(得分:0)

看到gameId进入useCallback deps数组很奇怪。我认为您不必在上面加上函数参数,而只需在函数定义中使用变量(props或useState值)即可。

要回答您的问题,取决于调用它时onGameOver函数传递的参数。