简体   繁体   English

(Expo) 从服务器获取数据后,React Native 不更新 state

[英](Expo) React Native not updating state after fetching data from server

I am creating an app in react-native.我正在 react-native 中创建一个应用程序。 I encounter with an issue in app.我在应用程序中遇到问题。 I am fetching data from server and updating my state with fetched data.我正在从服务器获取数据并使用获取的数据更新我的 state。 Data from server are console logging without any error.来自服务器的数据是控制台日志记录,没有任何错误。 However after setting state, react-native is not re-rendering my questions on screen.但是在设置 state 后,react-native 不会在屏幕上重新呈现我的问题。 But When i save my project from code editor, and expo reloads, the data (questions) are visible on screen.但是当我从代码编辑器中保存我的项目并重新加载 expo 时,数据(问题)在屏幕上可见。 I can't find solution or from where error is occuring.我找不到解决方案或从哪里发生错误。 Hope down below code and gif demonstrate my problem.希望下面的代码和 gif 能证明我的问题。

I Have remove some styling from code to reduce length but code is still long.我已经从代码中删除了一些样式以减少长度,但代码仍然很长。 Link to original file (code) can be find here link to code链接到原始文件(代码)可以在这里找到代码链接

错误图像

import React, { useEffect, useState } from "react";
import MultipleChoice from "react-native-multiple-choice-picker";
import {
  View,
  Text,
  (other imports),
  Button,
} from "react-native";
import LoadingScreen from "../Loading";
import firebase from "../../config/firebase";
import SERVER from "../../config/variable";

const Solution = ({ route, navigation }) => {
  const [isLoading, setIsLoading] = useState(false);
  const [questionData, setQuestionData] = useState([]);
  const [correctAnswer, setCorrectAnswer] = useState([]);
  const [wrongAnswer, setWrongAnswer] = useState([]);
  const [notAttempted, setNotAttempted] = useState([]);
  const [questionCount, setQuestionCount] = useState(0);
  const [allQuestion, setAllQuestion] = useState([]);
  const { examid } = route.params;

  const fetchSolution = async () => {
    setIsLoading(true);
    try {
      const response = await fetch(`${SERVER}admin/get/result-for-user`, {
        method: "POST",
        headers: {
          Accept: "Application/json",
          "Content-Type": "Application/json",
        },
        body: JSON.stringify({
          email: firebase.auth().currentUser.email,
          examid,
        }),
      });
      const responseData = await response.json();
      setQuestionData(responseData.data);
      setCorrectAnswer(await JSON.parse(responseData.result[0].correctAnsArr));
      const data1 = JSON.parse(responseData.result[0].wrongAnsArr);
      setWrongAnswer(data1);
      setNotAttempted(await JSON.parse(responseData.result[0].notAttemtedArr));
      const datafinal = await updateQuestion();
      setAllQuestion(datafinal);
    } catch (error) {
      console.log(error);
      Alert.alert("Server Error");
    }
    setIsLoading(false);
  };

  const updateQuestion = () => {
    var correctAnsArr = [];
    var wrongAnsArr = [];
    var notAttemtedArr = [];

    for (let i = 0; i < correctAnswer.length; i++) {
      for (let j = 0; j < questionData.length; j++) {
        if (correctAnswer[i].questionId === questionData[j].id) {
          questionData[j].selectedOption = correctAnswer[i].selectedOption;
          correctAnsArr.push(questionData[j]);
        }
      }
    }

    for (let i = 0; i < wrongAnswer.length; i++) {
      for (let j = 0; j < questionData.length; j++) {
        if (wrongAnswer[i].questionId === questionData[j].id) {
          questionData[j].selectedOption = wrongAnswer[i].selectedOption;
          wrongAnsArr.push(questionData[j]);
        }
      }
    }

    for (let i = 0; i < notAttempted.length; i++) {
      for (let j = 0; j < questionData.length; j++) {
        if (notAttempted[i].questionId === questionData[j].id) {
          questionData[j].selectedOption = notAttempted[i].selectedOption;
          notAttemtedArr.push(questionData[j]);
        }
      }
    }

    return [...correctAnsArr, ...wrongAnsArr, ...notAttemtedArr];
  };

  useEffect(() => {
    fetchSolution();
  }, []);

  const RenderQuestion = () => {
    return (
      <View>
        <View>
          {allQuestion.length > 0 ? (
            <View
              key={allQuestion[0].id}
              style={{
                width: Dimensions.get("window").width,
              }}
            >
              <View style={{ margin: 10 }}>
                <Text style={{ fontSize: 16 }}>
                  Question {questionCount + 1} of {allQuestion.length}
                </Text>
              </View>

              {allQuestion[questionCount].isQuestionImage === "1" ? (
                <View
                  style={{
                    width: Dimensions.get("window").width,
                    display: "flex",
                    flex: 1,
                    justifyContent: "center",
                    alignItems: "center",
                  }}
                >
                  <Image
                    style={{
                      height: Dimensions.get("window").height * 0.5,
                      width: Dimensions.get("window").width * 0.8,
                    }}
                    source={{
                      uri: allQuestion[questionCount].questionFile,
                    }}
                  />
                </View>
              ) : (
                <View>
                  <Text style={{ fontSize: 35, margin: 15 }}>
                    {allQuestion[questionCount].questionText}
                  </Text>
                </View>
              )}
              <RenderOption />
            </View>
          ) : (
            <View>
              <Text>No Data</Text>
            </View>
          )}
        </View>
      </View>
    );
  };

  const RenderOption = () => {
    return (
      <View>
        {allQuestion.length > 0 ? (
          <View>
            {allQuestion[questionCount].isOptionImage === "1" ? (
              <MultipleChoice
                direction={"column"}
                chosenIndex={allQuestion[questionCount].selectedOption}
                choices={[
                  <Image
                    style={{
                      height: Dimensions.get("window").height * 0.3,
                      width: Dimensions.get("window").width * 0.6,
                    }}
                    source={{
                      uri: allQuestion[questionCount].optionAFile,
                    }}
                  />,
                  <Image
                    style={{
                      height: Dimensions.get("window").height * 0.3,
                      width: Dimensions.get("window").width * 0.6,
                    }}
                    source={{
                      uri: allQuestion[questionCount].optionBFile,
                    }}
                  />,

                  <Image
                    style={{
                      height: Dimensions.get("window").height * 0.3,
                      width: Dimensions.get("window").width * 0.6,
                    }}
                    source={{
                      uri: allQuestion[questionCount].optionCFile,
                    }}
                  />,
                  <Image
                    style={{
                      height: Dimensions.get("window").height * 0.3,
                      width: Dimensions.get("window").width * 0.6,
                    }}
                    source={{
                      uri: allQuestion[questionCount].optionDFile,
                    }}
                  />,
                ]}
              />
            ) : (
              <MultipleChoice
                direction={"column"}
                chosenIndex={allQuestion[questionCount].selectedOption}
                choices={[
                  <Text>
                    {allQuestion[questionCount].optionAText}
                  </Text>,
                  <Text>
                    {allQuestion[questionCount].optionBText}
                  </Text>,
                  <Text>
                    {allQuestion[questionCount].optionCText}
                  </Text>,
                  <Text>
                    {allQuestion[questionCount].optionDText}
                  </Text>,
                ]}
              />
            )}
          </View>
        ) : (
          <View></View>
        )}
      </View>
    );
  };
  const Renderbutton = () => {
    return (
      <View>
        {questionCount != 0 ? (
          <View>
            <Button
              color="#f9a602"
              title="Prev"
              onPress={() => {
                setQuestionCount(() => questionCount - 1);
              }}
            />
          </View>
        ) : null}
        {questionCount + 1 < allQuestion.length ? (
          <View>
            <Button
              title="Next"
              onPress={() => {
                setQuestionCount(() => questionCount + 1);
              }}
            />
          </View>
        ) : null}

        <View>
          <Button
            color="red"
            title="Finish"
            onPress={() => navigation.navigate("ResultList")}
          />
        </View>
      </View>
    );
  };

  if (isLoading) {
    return <LoadingScreen />;
  }
  return (
    <SafeAreaView style={{ display: "flex", flex: 1 }}>
      <ImageBackground
        source={require("../../images/signup_screen.jpg")}
        style={{ flex: 1, resizeMode: "cover" }}
      >
        <View>
          <ScrollView>
            <RenderQuestion />
            <Renderbutton />
          </ScrollView>
        </View>
      </ImageBackground>
    </SafeAreaView>
  );
};

export default Solution;


At updateQuestion() function, what will be populate as allQuestion , you expect correctAnswer, questionData, ... states, to hold the data they got in fetchSolution() .updateQuestion() function 处,将填充为allQuestion的内容,您期望正确的答案correctAnswer, questionData, ...状态来保存他们在fetchSolution()中获得的数据。

The thing is, that setState functions are async calls, and while you get into updateQuestion() , from inside fetchSolution() , the state isn't what you expect it to be.问题是, setState函数是异步调用,当您进入updateQuestion()时,从fetchSolution()内部,state 并不是您期望的那样。

Adding an example of how use useEffect for waiting changes affect state and only then action will be fired添加一个使用useEffect等待更改如何影响 state 的示例,然后才会触发操作


  const Solution = ({ route, navigation }) => {
     const [questionData, setQuestionData] = useState([]);
     const [correctAnswer, setCorrectAnswer] = useState([]);
     const [allQuestion, setAllQuestion] = useState([]);

       const fetchSolution = async () => {
             setQuestionData(['something'])
             setCorrectAnswer(['something'])
           }

        useEffect(() => fetchSolution(), []);
        useEffect(() => correctAnswer.length && questionData.length && 
                         updateQuestion(), [correctAnswer, questionData]);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM