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

使用useContext时重置子组件状态:点击按钮后计算结果未重置的修复

问题:再次点击按钮后Analysis组件的Table保留旧计算结果并累加

首次点击DealWithHands组件中的按钮时功能正常,但再次点击后,Analysis组件内的table会保留之前的计算结果并基于此继续累加计算,这不符合预期。

我考虑过两种解决思路,但存在疑问:

  • 点击按钮时先卸载再重新挂载子组件,但不清楚实现方法,且担心性能开销
  • 由父组件控制子组件状态,但认为这并非合理方案,因为子组件状态与父组件无关

相关代码

DealWithHands组件

import { useState, ChangeEvent, useMemo } from "react";

import "./index.css";
import ShowResults from "../Show/ShowResults";

function deal() {
    /* get boards */
}

export default function DealWithHands() {
    const [board_size, setBoard_size] = useState<number>(1);
    const [boards, setBoards] = useState<any[][]>([]);

    function handleSize(e: ChangeEvent) {
        setBoard_size(Number((e.target as HTMLInputElement).value));
    }

    function handleClick() {
        setBoards(deal(Number(board_size)));
    }

    return (
        <>
            <div className="deal-setting">
                <input value={board_size} onChange={handleSize} placeholder="input your number here"></input>
                <button onClick={handleClick}>Get new boards</button>
            </div>
            {useMemo(() => <ShowResults all_boards={boards.map((x) => { return { board: x } })} />
                , [boards])}
        </>
    )
}

ShowResults组件

import React, { createContext } from "react";
import ShowAllBoards from "../../Components/ShowAllBoards/ShowAllBoards";
import Hand from "../Deal/Hand";
import Analysis from "../Analysis/Analysis";

export interface CompleteBoard {
    board: Hand[];
    ddtricks?: (string | number)[][] | string;
}

interface ShowResultsProps {
    all_boards: CompleteBoard[];
}

interface ShowResultsContextProps {
    all_boards: CompleteBoard[];
}

export const ShowResultsContext = createContext<ShowResultsContextProps>({ all_boards: []});

function ShowResults(props: ShowResultsProps) {
    const { all_boards, ...rest } = props;
    return (
        <ShowResultsContext.Provider value={{ all_boards }}>
            <Analysis />
            <ShowAllBoards {...rest} />
        </ShowResultsContext.Provider>
    )
}

export default React.memo(ShowResults);

Analysis组件

import React, { useContext,  useMemo, useState } from "react";
import ShowTricks from "../../Components/PlayBoard/ShowTricks";
import { analyzeOffline } from "../../Utils/utils";
import { ShowResultsContext } from "../Show/ShowResults";
import "./analysis.css"

function MatrixAdd(a: number[][], b: (string | number)[][]) {
  const ret: number[][] = [];
  for (let i = 0; i < a.length; ++i) {
    ret[i] = [];
    for (let j = 0; j < a[0].length; ++j) {
      ret[i][j] = a[i][j] + Number(b[i][j]);
    }
  }
  return ret;
}

function MatrixDivide(a: number[][], b: number) {
  const ret: number[][] = [];
  for (let i = 0; i < a.length; ++i) {
    ret[i] = [];
    for (let j = 0; j < a[0].length; ++j) {
      ret[i][j] = a[i][j] / b;
    }
  }
  return ret;
}

function Analysis() {
  const [table, setTable] = useState(new Array(4).fill(0).map(() => new Array(5).fill(0)));
  const context = useContext(ShowResultsContext);
  const { all_boards } = context;
  useMemo(() => {
    async function countTricks() {
      let tmp: number[][] = [];
      table.forEach((row) => {
        tmp.push([...row]);
      })
      for (const board of all_boards) {
        if (!board.ddtricks) {
          board.ddtricks = await analyzeOffline(board.board);
        }
        tmp = MatrixAdd(tmp, board.ddtricks as (string | number)[][]);
      }
      return Promise.resolve(tmp);
    }
    countTricks().then((res) => {
      if (all_boards.length > 0) {
        setTable(MatrixDivide(res, all_boards.length));
      }
    })
  }, [all_boards])

  return (
    <fieldset className="showStatistics">
      <legend>The average tricks:</legend>
      <ShowTricks ddtricks={table} />
    </fieldset>
  )
}

export default Analysis;

解决方案

问题根源在于:

  1. Analysis组件中每次计算新牌局时,tmp是基于之前的table状态初始化的,导致新结果与旧结果累加;
  2. 使用useMemo处理异步状态更新属于误用,useMemo用于缓存计算结果,而非处理副作用。

修改步骤如下:

1. 修正Analysis组件的计算逻辑

将useMemo替换为useEffect(专门处理异步、状态更新这类副作用),并每次从全0矩阵开始计算新牌局的平均值,而非基于旧table:

import React, { useContext, useEffect, useState } from "react";
import ShowTricks from "../../Components/PlayBoard/ShowTricks";
import { analyzeOffline } from "../../Utils/utils";
import { ShowResultsContext } from "../Show/ShowResults";
import "./analysis.css"

function MatrixAdd(a: number[][], b: (string | number)[][]) {
  const ret: number[][] = [];
  for (let i = 0; i < a.length; ++i) {
    ret[i] = [];
    for (let j = 0; j < a[0].length; ++j) {
      ret[i][j] = a[i][j] + Number(b[i][j]);
    }
  }
  return ret;
}

function MatrixDivide(a: number[][], b: number) {
  const ret: number[][] = [];
  for (let i = 0; i < a.length; ++i) {
    ret[i] = [];
    for (let j = 0; j < a[0].length; ++j) {
      ret[i][j] = a[i][j] / b;
    }
  }
  return ret;
}

function Analysis() {
  const [table, setTable] = useState(new Array(4).fill(0).map(() => new Array(5).fill(0)));
  const context = useContext(ShowResultsContext);
  const { all_boards } = context;

  useEffect(() => {
    async function countTricks() {
      // 每次计算都从全0矩阵开始,避免旧结果累加
      let tmp = new Array(4).fill(0).map(() => new Array(5).fill(0));
      // 复制数组避免直接修改context中的原对象
      const targetBoards = [...all_boards];

      for (const board of targetBoards) {
        if (!board.ddtricks) {
          const ddtricks = await analyzeOffline(board.board);
          board.ddtricks = ddtricks;
        }
        tmp = MatrixAdd(tmp, board.ddtricks as (string | number)[][]);
      }

      if (targetBoards.length > 0) {
        setTable(MatrixDivide(tmp, targetBoards.length));
      } else {
        // 无牌局时重置table为全0
        setTable(new Array(4).fill(0).map(() => new Array(5).fill(0)));
      }
    }

    countTricks();
  }, [all_boards]);

  return (
    <fieldset className="showStatistics">
      <legend>The average tricks:</legend>
      <ShowTricks ddtricks={table} />
    </fieldset>
  )
}

export default Analysis;

2. 可选优化:避免修改Context中的原对象

如果希望完全避免直接修改all_boards中的对象,可以在DealWithHands组件中提前处理牌局的计算,或者使用useReducer管理状态,防止状态突变引发的潜在问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:42:05