You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React实现Wordle克隆:兄弟组件如何监听全局状态变化

问题根因

状态无法同步渲染是三个核心问题导致的:

  • 全局状态没有使用React响应式状态API:直接把普通数组传给Context Provider,直接修改原数组不会触发React重渲染检测,所有消费Context的组件都收不到更新通知。
  • 输入位置追踪变量失效:currentRow和currentTile是Keypad组件内的普通局部变量,每次组件重渲染都会被重置为0,既无法持久化记录当前输入位置,也无法被其他组件共享。
  • Row组件硬编码只渲染第一行:代码写死取rowData[0],后续行的输入永远不会被展示,也没有实现全网格遍历渲染逻辑。

修复方案

1. 重构AppContext.js,用响应式状态管理全局数据

把网格数据、当前输入位置都放到Context的状态里,同时暴露状态更新方法,确保所有修改都能触发组件重渲染:

// AppContext.js
import { createContext, useState } from "react";

// 初始化6行5列的空网格
const initGuessRows = Array(6).fill(null).map(() => Array(5).fill(''));

export const AppContext = createContext();

const AppContextProvider = (props) => {
  // 网格数据状态
  const [guessRows, setGuessRows] = useState(initGuessRows);
  // 当前输入位置状态:记录当前激活的行、格子索引
  const [position, setPosition] = useState({ currentRow: 0, currentTile: 0 });

  // 全局输入字母方法
  const addLetter = (letter) => {
    const { currentRow, currentTile } = position;
    // 边界判断:行/格子超出网格范围直接返回
    if (currentRow >= 6 || currentTile >= 5) return;
    // 拷贝新数组,禁止直接修改原状态
    const newRows = [...guessRows];
    newRows[currentRow] = [...newRows[currentRow]];
    newRows[currentRow][currentTile] = letter;
    // 更新网格状态
    setGuessRows(newRows);
    // 输入位置后移一位
    setPosition(prev => ({...prev, currentTile: prev.currentTile + 1}));
  };

  return(
    <AppContext.Provider value={{
      guessRows,
      position,
      addLetter
    }}>
      {props.children}
    </AppContext.Provider>
  )
}

export default AppContextProvider;

2. 修改Keypad.js,调用全局方法更新状态

移除组件内私有的位置变量和直接修改数组的逻辑,从Context获取全局方法处理点击事件:

// Keypad.js
import { useContext } from "react"
import { AppContext } from "../AppContext"

const Keypad = () => {
  const { addLetter } = useContext(AppContext);

  const letters = [
    "Q","W","E","R","T","Y","U","I","O","P",
    "A","S","D","F","G","H","J","K","L",
    "Z","X","C","V","B","N","M"
  ];

  return (
    <div className="keyboard-container">
      {letters.map((letter, index) => {
        return (
          <div className="key" key={letter} onClick={() => addLetter(letter)}>
            {letter}
          </div>
        )
      })}
      {/* 后续可在此添加删除、提交按键,对应新增Context方法即可 */}
    </div>
  )
};
 
export default Keypad;

3. 修改Row.js,通过props接收行索引渲染对应行

移除硬编码的第一行逻辑,让Row组件接收rowIndex参数,渲染对应行的格子数据:

// Row.js
import { useContext } from "react";
import { AppContext } from "../AppContext";

const Row = ({ rowIndex }) => {
  const { guessRows } = useContext(AppContext);
  const rowData = guessRows[rowIndex];

  return (
    <div className="row">
      {rowData.map((letter,index) => {
        return(
          <div className="tile" key={index} id={`row-${rowIndex}-tile-${index}`}>{letter}</div>
        )
      })}
    </div>
  )
}
 
export default Row;

4. 补充Grid.js遍历渲染逻辑

Grid组件负责遍历6行网格,给每个Row传递对应的行索引:

// Grid.js
import Row from "./Row";

const Grid = () => {
  return (
    <div className="grid-container">
      {Array(6).fill(null).map((_, index) => <Row key={index} rowIndex={index} />)}
    </div>
  )
}

export default Grid;

5. 检查根组件配置

确保入口文件(通常是index.js)中用AppContextProvider包裹App组件,否则所有组件都无法访问Context数据:

// index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import AppContextProvider from './AppContext';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <AppContextProvider>
      <App />
    </AppContextProvider>
  </React.StrictMode>
);

最后在App.js中引入Grid、Keypad组件调整布局即可。


注意事项

  • 永远不要直接修改React状态/Context里的对象、数组,必须创建新的拷贝再更新,否则React无法检测到状态变化。
  • 跨组件共享的状态和变量,必须放到全局状态或者共同的父组件里,不要在单个子组件里用普通变量存储公共逻辑依赖的数据,否则既无法持久化也无法跨组件共享。
  • 渲染列表时要加唯一的key属性,动态增删的列表不要用遍历索引当key,本项目固定6行网格用索引不会出现异常。

内容的提问来源于stack exchange,提问作者Josh Simon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:42:25