我在学习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}) />
我可能做错了什么?
谢谢
答案 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
-作为参数收到请注意,useCallback
是useMemo
的便捷版本,即它正在制作/获取函数的缓存版本。依赖项数组用于告诉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函数传递的参数。