React Hooks状态为何在Guess组件显示后恢复初始值?
猜数字游戏状态重置问题解决方案
问题描述
开发猜数字游戏时,点击盒子5次生成随机数,组件挂载时生成包含5个随机数的初始数组;输入数字后检查两个数组是否包含该数字并对应上色。但当Guess组件显示后,所有Hook状态都会恢复为初始值。
核心原因
Guess组件内部重复调用了useHook(),每个useHook调用都会创建完全独立的状态实例。也就是说,Main组件和Guess组件使用的是两套毫无关联的状态,并非Main的状态被重置,而是Guess在使用自己的全新初始状态。
解决方案
1. 统一状态管理,移除Guess内的useHook调用
让所有状态和逻辑仅在Main组件的useHook实例中维护,通过props将需要的方法和状态传递给Guess组件。
修改后的代码
Main.tsx
import { Guess } from "./Guess"; import { useHook } from "./Hook"; import { Loading } from "./Loading"; import "./styles.css"; export const Main = () => { // 取出所有需要的状态和方法 const { loading, count, handleClick, randArr, handleKeyUp, guess } = useHook(); return ( <div className="main"> {!loading && count < 5 && ( <div className='click-container'> {Array.from({ length: 5 }).fill('').map((_, i: number) => <div onClick={handleClick} className='box' key={i}>Click</div> )} </div> )} {loading && <Loading count={count} />} {!loading && count >= 5 && ( // 将handleKeyUp和guess通过props传递给Guess <Guess arr={randArr} handleKeyUp={handleKeyUp} guess={guess} /> )} </div> ); };
Guess.tsx
import React, { useEffect } from "react"; import "./styles.css"; // 复用Hook中的guess类型 type guessType = { keyNum: number; isContain: boolean; }; // 更新props类型,包含传递的方法和状态 type props = { arr: number[]; handleKeyUp: (e: { key: string }) => void; guess: guessType[]; }; export const Guess: React.FC<props> = (props) => { const { handleKeyUp, guess } = props; useEffect(() => { window.addEventListener("keyup", handleKeyUp); return () => { window.removeEventListener("keyup", handleKeyUp); }; }, [handleKeyUp]); console.log(props.arr, " this is props arr "); return ( <div className="content"> <div> <p>Guesses: </p> <div className="guess-list"> {guess.map((item, i: number) => <p key={i} className={item.isContain ? 'guess-num-true': 'guess-num-false'} > {item.keyNum} </p> )} </div> </div> </div> ); };
Hook.tsx(修复类型匹配问题)
import { useEffect, useState } from 'react' export const useHook = () => { type guessType = { keyNum: number isContain: boolean } const [disable, setDisable] = useState(true) const [randArr, setRandArr] = useState<number[]>([]) const [initialArr, setInitialArr] = useState<number[]>([]) const [count, setCount] = useState<number>(0) const [loading, setLoading] = useState(true) const [guess, setGuess] = useState<guessType[]>([]) const randomNum = () => { return Math.floor(Math.random() * (9 - 0 + 1) + 0); } useEffect(() => { const handleInitialArr = () => { for (let i = 0; i < 5; i++) { let num = randomNum() setInitialArr((prev) => [...prev, num]) } } handleInitialArr() }, []) const handleClick = () => { if (!disable) { let num = randomNum() setRandArr((prev)=> [...prev, num]) setCount((prev) => prev + 1) setDisable(true) setLoading(true) } } useEffect(()=> { const handleLoading = () => { setTimeout(() => { setLoading(false) }, 500) } const handleRound = () => { setDisable(false) } handleLoading() handleRound() }, [count]) const handleKeyUp = ({key}) => { const isNumber = /^[0-9]$/i.test(key) if (isNumber) { // 修复:将字符串key转为数字,避免和数字数组的includes判断不匹配 const numKey = parseInt(key, 10) if (randArr.includes(numKey) && initialArr.includes(numKey)) { setGuess((prev) => [...prev, {keyNum: numKey, isContain: true}]) console.log(' they both have this number') } else { setGuess((prev) => [...prev, {keyNum: numKey, isContain: false}]) console.log(' they both do not contain this number ') } } } return { count, loading, handleClick, randArr, handleKeyUp, guess } }
关键修改说明
- 确保状态唯一:所有状态仅在
Main组件的useHook实例中维护,避免多实例导致的状态割裂 - 数据统一传递:通过props将方法和状态传递给
Guess,保证两者使用同一套数据 - 修复类型错误:将键盘事件的字符串key转为数字,解决数组
includes判断失效的问题
内容的提问来源于stack exchange,提问作者orrigenda
相关产品推荐
相关产品推荐

