如何在记忆游戏场景中避免使用带依赖项的useEffect钩子
问题
我正在开发一款简易记忆游戏应用,想了解在以下场景中是否可避免使用带依赖项的useEffect钩子:用户点击卡片时触发函数,将点击的卡片存入两个状态变量之一,后续用于比对。
点击处理函数代码:
const handleClick = (card: Card) => { if (card.id !== choiceOne?.id) { if (choiceOne) return setChoiceTwo(card) return setChoiceOne(card) } }
当前实现中,当choiceTwo更新时,useEffect会触发执行卡片比对及其他必要操作,代码如下:
const useAsyncUseEffect = (fn: () => void, depends: Parameters<typeof useEffect>[1]) => { useEffect(() => { fn() }, [depends]) } useAsyncUseEffect(async () => { if (choiceOne && choiceTwo) { setDisabled(true) if (choiceOne.src === choiceTwo.src) { setMatchCount(prev => prev + 1) setCards(prev => prev.map(card => (card.src === choiceOne.src ? { ...card, match: true } : card)) ) resetSelection() } else { await sleep(1000) resetSelection() } } }, [choiceTwo])
解决方案
完全可以避免使用useEffect,把比对逻辑直接整合到点击处理函数里就行,这样代码逻辑更连贯,也不用依赖状态更新触发副作用。
修改后的点击处理函数可以写成这样:
const handleClick = async (card: Card) => { // 点击已选中的卡片、已匹配的卡片或处于禁用状态时直接返回 if (card.id === choiceOne?.id || card.match || disabled) return // 处理第一次选择 if (!choiceOne) { setChoiceOne(card) return } // 处理第二次选择 setChoiceTwo(card) setDisabled(true) // 比对两张卡片 if (choiceOne.src === card.src) { setMatchCount(prev => prev + 1) setCards(prev => prev.map(cardItem => (cardItem.src === choiceOne.src ? { ...cardItem, match: true } : cardItem)) ) resetSelection() setDisabled(false) } else { await sleep(1000) resetSelection() setDisabled(false) } }
说明
- 把原本在useEffect里的异步逻辑直接放到
handleClick中,用户完成第二次点击时立刻执行比对,不需要等状态更新触发副作用。 - 增加了对
disabled和card.match的判断,避免在卡片比对过程中重复点击,逻辑更严谨。 - 移除了自定义的
useAsyncUseEffect和相关副作用依赖,代码结构更简洁,状态变化的因果关系更清晰。
内容的提问来源于stack exchange,提问作者ReanuKeeves
相关产品推荐
相关产品推荐

