将变量传递给 React useCallback 钩子



我在学习 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"放在我的依赖项数组中,但它在控制台中始终是"未定义的".log。

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

const gameId = useState(123);

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

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

我可能做错了什么?

谢谢

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

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

这将在传递给React.useCallback()的函数之外"影子"gameId的值。

传递给React.useCallback()的依赖项数组不会隐式传递给正在创建的函数。该数组用于确定传递给该特定渲染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数组将导致在组件生命周期中只执行一次效果的原因 - 因为数组值永远不会更改,因此效果永远不会重新运行。

更新以响应您的编辑

您添加了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]的(或者[setPages, arrayMove]如果我关于 arrayMove 是导入的猜测是错误的),因为这是函数引用的唯一非常量值,没有作为参数传入。

当你将actualOnGameOver传递到useCallback时,结果是一个具有相同签名的函数,所以你会以相同的方式调用onGameOver,例如

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

修复依赖项数组后,如果gameId仍未定义,则应查看onGameOver函数之外的代码。确保在调用onGameOver时传递gameId的值。

将gameId放入useCallback deps数组中很奇怪。我认为您不必在其上放置函数参数,而只需在函数定义中使用的变量(props 或 useState 值)即可。

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

相关内容

  • 没有找到相关文章

最新更新