
本文详解为何 nextQuestion 触发后题目频繁重置,并提供基于状态管理与数据生命周期控制的可靠解决方案,核心在于将外部依赖(如随机抽题结果)移出组件内部、避免每次渲染重复调用数据函数。
本文详解为何 nextquestion 触发后题目频繁重置,并提供基于状态管理与数据生命周期控制的可靠解决方案,核心在于将外部依赖(如随机抽题结果)移出组件内部、避免每次渲染重复调用数据函数。
在你提供的代码中,questions = requestQuestions(category) 被直接写在组件函数体顶层,属于每次渲染都会执行的副作用逻辑。而 React 在调用 setCurrentQuestion() 更新状态时,会立即触发组件重新执行整个函数体(即重渲染)。这意味着:
- 每次点击按钮 →
nextQuestion()→setCurrentQuestion()→ 组件重渲染 - 重渲染时再次执行
requestQuestions(category)→ 重新抽样 10 道题 →questions数组完全改变 - 即使
currentQuestion索引递增,它指向的已是全新数组中的不同题目 → 表现为“题目乱跳”或“反复出现同一题但顺序错乱”
这本质上是将有副作用的数据获取逻辑错误地置于渲染逻辑中,违反了 React 的数据流原则。
✅ 正确做法:分离数据生成与状态管理
应确保 questions 在组件生命周期内保持稳定,仅在必要时(如 category 变化)重新生成。推荐两种生产级方案:
方案一:由父组件预加载并透传(推荐,简洁可控)
// ParentScreen.tsx
import { Test } from './Test';
import { requestQuestions } from '../constants/support';
export default function TestWrapper() {
const { category } = useLocalSearchParams();
// ✅ 在父组件中一次性生成题目,保证稳定性
const questions = useMemo(() => requestQuestions(category), [category]);
return <Test questions={questions} />;
}// Test.tsx
import React, { useState } from 'react';
import { View, Text, Image, Button, SafeAreaView } from 'react-native';
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.');
}
setCurrentQuestion(nextIndex);
};
const { id, img, question, anwsers } = questions[currentQuestion];
return (
<SafeAreaView style={styles.area}>
<Image
source={img || { uri: "https://www.liaros-drive.gr/test/images/pic0484.jpg" }}
style={styles.image}
/>
<Text style={styles.title}>{question}</Text>
<SafeAreaView style={styles.buttonContainer}>
{anwsers?.map((answer, index) => (
<Button
key={index}
title={`${index + 1}) ${answer}`}
onPress={nextQuestion}
/>
))}
</SafeAreaView>
</SafeAreaView>
);
};
export default Test;? 关键点:
questions不再是组件内部变量,而是不可变的 props 输入,React 不会因自身状态更新而重新计算它。
方案二:使用 useMemo 在组件内缓存(适用于无合适父组件场景)
const Test = () => {
const { category } = useLocalSearchParams();
// ✅ 使用 useMemo 缓存,仅当 category 变化时重新执行
const questions = useMemo(() => requestQuestions(category), [category]);
const [currentQuestion, setCurrentQuestion] = useState(0);
// ...其余逻辑保持不变
};⚠️ 注意事项:
- ❌ 避免在渲染体中直接调用
requestQuestions()(无缓存、无依赖追踪); - ✅ 若使用
useMemo,务必正确添加依赖项[category],否则缓存失效; - ✅
requestQuestions应是纯函数(不依赖外部可变状态),确保相同输入返回相同输出; - ? 在
support.js中,注意==应改为===,并检查category类型是否为字符串(useLocalSearchParams()返回值为字符串或字符串数组)。
通过以上重构,questions 数组在整个测试流程中保持恒定,currentQuestion 状态才能真正反映用户在同一套题目中的进度,彻底解决“题目反复变化”的问题。


















