
本文详解 React 组件重渲染导致 requestQuestions() 被反复调用的问题根源,并提供基于 props 缓存问题列表的可靠解决方案,确保题库稳定、索引准确、用户体验连贯。
本文详解 react 组件重渲染导致 `requestquestions()` 被反复调用的问题根源,并提供基于 props 缓存问题列表的可靠解决方案,确保题库稳定、索引准确、用户体验连贯。
在 React(包括 Expo Router)中,每次调用 setState(如 setCurrentQuestion)都会触发组件函数重新执行。你的 Test 组件中,questions = requestQuestions(category) 位于函数体顶层,因此每次重渲染都会重新执行该函数——即使 category 未变,sampleSize() 也会随机抽样生成全新问题数组,导致 currentQuestion 索引“错位”,看似题目乱序跳转。
根本原因在于:问题数据未被稳定缓存,而是随每次渲染动态生成。useState 只管理 currentQuestion 这一索引,却无法控制 questions 数组本身的稳定性。
✅ 正确做法是将问题列表作为 不可变的 prop 传入组件,使其脱离渲染周期影响:
// 父组件(例如 _layout.tsx 或路由页面)
import { Test } from './Test'; // 假设已改为命名导出
import { requestQuestions } from '../constants/support';
export default function TestWrapper() {
const { category } = useLocalSearchParams();
// ✅ 在父组件中一次性获取并固定 questions
const questions = requestQuestions(category);
return <test questions="{questions}"></test>;
}
// Test.tsx —— 修改为接收 questions prop
import React, { useState } from 'react';
import { useLocalSearchParams } from 'expo-router';
import { COLORS } from '../constants';
import { View, Text, Image, Button, StyleSheet, SafeAreaView } from 'react-native';
import Header from '../constants/testHeader';
// ✅ 移除内部 requestQuestions 调用,改由 props 输入
interface Question {
id: string;
img?: any;
question: string;
anwsers: string[];
}
interface TestProps {
questions: Question[];
}
const Test = ({ questions }: TestProps) => {
const [currentQuestion, setCurrentQuestion] = useState(0);
const nextQuestion = () => {
const nextIndex = (currentQuestion + 1) % questions.length;
if (nextIndex === 0) {
alert('All questions are finished.');
} else {
setCurrentQuestion(nextIndex);
}
};
const { id, img, question, anwsers } = questions[currentQuestion];
return (
<safeareaview style="{styles.area}"><image source="{img" uri: style="{styles.image}"></image><text style="{styles.title}">{question}</text><safeareaview style="{styles.buttonContainer}">
{anwsers?.map((answer, index) => (
<button key="{index}" title="{`${index" onpress="{nextQuestion}"></button>
))}
</safeareaview></safeareaview>
);
};
export default Test;
⚠️ 注意事项:
- 不要使用
useMemo(() => requestQuestions(category), [category])在Test内部缓存——虽可减少调用次数,但sampleSize()的随机性仍会导致不同渲染周期返回不同数组,无法保证跨重渲染一致性; - 若需支持「重新开始测试」,应在父组件中提供重置逻辑(如
key变更或显式刷新questions),而非在子组件内重复抽样; - 确保
questions.json数据结构与组件解构一致(如anwsers拼写应为answers,原文中为笔误,生产环境请校对字段名)。
通过将动态数据提取至父级并以 props 注入,你不仅解决了题目跳变问题,也践行了 React “数据向下流动、状态就近管理” 的设计原则——让 Test 专注交互逻辑,让数据供给职责清晰分离。











