You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React + TypeScript 技术问询:如何实现已评分游戏数量统计及子组件向父组件传递计数

解决已评分游戏统计与子组件Props/回调使用问题

咱们一步步拆解你遇到的问题,逐个解决:

1. 修复子组件的Props与回调定义错误

你现在的子组件参数写法完全不符合React组件的规范——React函数组件仅接收一个props对象作为参数,不能拆成两个独立参数。咱们重新梳理类型定义:

// 子组件(BoardGameSurvey.tsx)
import React from "react";
import { Game } from "../Parent"; // 记得导入父组件的Game接口
import { useSelector } from "react-redux";
import { Grid, Card, CardActionArea, CardMedia, CardContent, Box } from "@mui/material";
// 导入你的自定义组件
import StyledCard from "./StyledCard";
import ImgWrapper from "./ImgWrapper";
import GameTitle from "./GameTitle";
import StarRating from "./StarRating";

// 统一定义子组件的Props类型
interface BoardCardProps {
  game: Game;
  parentCallback: (scoreChange: number) => void; // 回调接收评分变化值:+1(新增评分)、-1(取消评分)
}

export default function BoardCard(props: BoardCardProps) {
  const userno = useSelector((state: any) => state.user.userNo); // 替换为你实际的Redux RootState类型

  // 处理评分变化:对比新旧评分,计算统计数的变化
  const setStarRatings = (newScore: number, oldScore: number) => {
    // 从无评分到有评分:统计数+1
    if (oldScore === 0 && newScore > 0) {
      props.parentCallback(1);
    }
    // 从有评分到取消评分:统计数-1
    else if (oldScore > 0 && newScore === 0) {
      props.parentCallback(-1);
    }
    // 修改评分但未取消:统计数不变,无需处理
  };

  return (
    <Grid item xs={12} sm={4} md={3} lg={2.3}>
      <StyledCard variant="outlined">
        <CardActionArea>
          <ImgWrapper>
            <CardMedia
              sx={{ position: "absolute", width: "100%", height: "100%", objectFit: "contain" }}
              component="img"
              image={props.game.gameImg}
              alt={props.game.gameName}
            />
          </ImgWrapper>
          <CardContent>
            <Grid container justifyContent="center">
              <GameTitle>{props.game.gameName}</GameTitle>
              <Box width="100%" />
              {/* 确保StarRating组件能传递新旧评分值给setStarRatings */}
              <StarRating
                initStarRate={0}
                gameNo={props.game.gameNo}
                userNo={userno}
                size={35}
                parentCallback={setStarRatings}
              />
            </Grid>
          </CardContent>
        </CardActionArea>
      </StyledCard>
    </Grid>
  );
}

核心调整点:

  • gameparentCallback合并到同一个Props接口中
  • 回调改为接收评分变化值,支持新增和取消评分的双向统计
  • 子组件通过对比新旧评分,精准传递统计数的变化量

2. 修复父组件的统计逻辑

你原来的countHandler存在闭包问题(依赖旧的count值),且未处理取消评分的场景。改成函数式更新来解决:

// 父组件(Parent.tsx)
export default function Survey() {
  const [gameList, setGameList] = useState<Game[]>([]);
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(0);
    setGameList(tempData.gameList);
    // 可选:初始化时从API拉取用户已评分数量,避免子组件逐个回调统计
    // fetchUserRatedGameCount().then(cnt => setCount(cnt));
  }, []);

  const countHandler = (scoreChange: number) => {
    // 使用函数式更新,确保拿到最新的count值,同时避免负数
    setCount(prevCount => Math.max(prevCount + scoreChange, 0));
  };

  // ...其余代码保持不变
}

3. 更高效的已评分统计方案

除了子组件回调的方式,还有两种更优的方案:

方案一:依赖Redux全局状态统计

如果你的已评分游戏ID已经存在Redux全局状态中(比如state.user.ratedGameIds是一个数组),父组件可以直接计算统计数,无需子组件回调:

// 父组件中
const ratedGameIds = useSelector((state: any) => state.user.ratedGameIds);
const count = ratedGameIds.length; // 直接从全局状态计算,无需useState维护

这种方式无需额外的状态同步,统计数会随全局状态自动更新,性能更优。

方案二:初始化预统计+实时增量更新

如果从API获取游戏列表和用户已评分数据,初始化时先拉取已评分数量,再通过回调处理实时修改:

useEffect(() => {
  // 1. 获取游戏列表
  fetchGameList().then(list => setGameList(list));
  // 2. 预加载用户已评分数量
  fetchUserRatedCount().then(cnt => setCount(cnt));
}, []);

// 仅处理实时的评分变化
const countHandler = (scoreChange: number) => {
  setCount(prev => Math.max(prev + scoreChange, 0));
};

这种方式减少了初始渲染时的回调次数,避免每个子组件都触发一次统计。

额外优化建议

  • 确保StarRating组件能正确传递新旧评分值,否则无法准确判断统计数变化
  • 父组件的Completed按钮可以根据count值添加禁用逻辑:disabled={count === 0},提升用户体验
  • 若使用TypeScript,建议定义全局的Redux状态类型,替换any类型,避免类型错误

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

火山引擎 最新活动