为什么React Native乘法练习项目中compareQA的qaIndex仅返回0或-1
问题排查与修复方案
存在的问题
- React state更新是异步操作,set后无法立刻读取最新值
createQuestionsArray函数中循环调用setQuestionsArray、setAnswersArray后立刻执行askQuestion,此时state还未完成更新,questionsArray仍是初始空数组,随机取值会得到undefined,后续indexOf查找undefined自然返回-1。compareQA函数中刚执行setQAIndex就立刻用qaIndex取值,此时拿到的是上一次渲染的旧值,不是刚计算出的索引,导致比对逻辑永远不生效。
- 直接修改state原值违反React不可变原则
直接对state中的questionsArray、answersArray调用splice原地修改数组,会导致React状态更新异常。 - 数据类型不匹配
TextInput返回的输入值是字符串类型,和answersArray中存储的数字直接比对,存在隐式转换的潜在风险。
修复后的代码示例
import React, { useState, useEffect } from "react"; import { StyleSheet, Text, View, Pressable, TextInput } from 'react-native'; import { StatusBar } from 'expo-status-bar'; import { NavigationContainer } from '@react-navigation/native'; import styles from '../Components/AppStyle'; import {LittleSpace05,LittleSpace1, LittleSpace4} from '../Components/LittleSpace'; import changeSize from '../Components/ChangeSize'; function RouletteExercice1 ({ route, navigation }){ const { multiplyArray, multBy} = route.params; const [adaptTitleFontSize, setAdaptTitleFontSize] = useState({ fontSize: 16, }); const [adaptButtonFontSize, setAdaptButtonFontSize] = useState({ fontSize: 16, }); const getSize = (event, divide, thing) => { if (thing == 'title') { setAdaptTitleFontSize(changeSize(event.nativeEvent.layout, divide)) } else { setAdaptButtonFontSize(changeSize(event.nativeEvent.layout, divide)) } }; const [questionsArray,setQuestionsArray] = useState([]); const [answersArray,setAnswersArray] = useState([]); const [mQuestion,setMQuestion] = useState(); const [textInputAnswer, setTextInputAnswer] = useState(); useEffect(() => { createQuestionsArray(); },[]); // 监听题目数组变化,有值时抽第一道题 useEffect(() => { if(questionsArray.length > 0) { askQuestion() } }, [questionsArray]) function createQuestionsArray() { // 先生成完整的题目、答案数组,一次性更新state const questions = [] const answers = [] let m = 0; while (m < multiplyArray.length) { let b = 1; while (b < multBy+1) { let quest = multiplyArray[m]+'x'+b; let rep = multiplyArray[m]*b; questions.push(quest); answers.push(rep); b++ } m++ } setQuestionsArray(questions); setAnswersArray(answers); } function askQuestion(){ setMQuestion(questionsArray[Math.floor(Math.random()*questionsArray.length)]); } function compareQA(){ // 直接在本地计算当前索引,不需要存到state const currentIndex = questionsArray.indexOf(mQuestion); if(currentIndex === -1) return // 输入转数字后再做严格比对 if (answersArray[currentIndex] === Number(textInputAnswer)) { // 生成新数组更新state,不修改原值 const newQuestions = [...questionsArray] const newAnswers = [...answersArray] newQuestions.splice(currentIndex,1); newAnswers.splice(currentIndex,1); setQuestionsArray(newQuestions); setAnswersArray(newAnswers); console.log(currentIndex); setTextInputAnswer('') // 还有剩余题目就抽下一道 if(newQuestions.length > 0) { setMQuestion(newQuestions[Math.floor(Math.random()*newQuestions.length)]); } else { alert('所有题目已完成!') } } else { alert('答案错误,请重试') } } return( <View style={{ flex: 1, flexDirection: 'column'}}> <StatusBar style="auto" /> <View style={{flex: 1}}> </View> <View style={{flex: 4, alignItems: 'center', justifyContent: 'center'}}> <LittleSpace1/> <LittleSpace1/> <View onLayout={(event) => {getSize(event,2,'title')}} style={[styles.setViewRE1, {alignItems: 'center'}]}> <Text style={[styles.textExercice, adaptTitleFontSize,{fontWeight: 'bold'}]}>{mQuestion}</Text> </View> <LittleSpace05/> <View style={[styles.setViewRE1, {alignItems: 'center'}]}> <Text style={[styles.textExercice, adaptTitleFontSize,{fontWeight: 'bold'}]}>=</Text> </View> <LittleSpace05 /> <View onLayout={(event) => {getSize(event, 3,'button')}} style={styles.setViewRE1}> <LittleSpace4/> <TextInput onChangeText={text => setTextInputAnswer(text.replace(/[^0-9]/g, ''))} value={textInputAnswer} placeholder=" perso" textAlign={'center'} keyboardType="numeric" style={[ styles.inputRE1, adaptButtonFontSize]} type='number' /> <LittleSpace1/> <Pressable style={styles.buttonRE1} onPress={() => {compareQA();}} > <Text style={[styles.textExercice, adaptButtonFontSize]}>OK</Text> </Pressable> <LittleSpace1/> </View> <LittleSpace1/> <LittleSpace1/> <LittleSpace4/> </View> <View style={{flex: 1,}}> </View> </View> ) } export default RouletteExercice1;
内容的提问来源于stack exchange,提问作者Sevlac
相关产品推荐
相关产品推荐

