重定向到新页面时如何从Redux存储加载数据

时间:2019-09-24 20:33:21

标签: reactjs typescript redux react-redux react-router

我有两页;第一个称为QuizHomePage,其中包含欢迎消息和一个按钮,允许用户开始测验。

QuizHomePage.tsx:

import Button from "@material-ui/core/Button";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import React from "react";
import { connect } from "react-redux";
import { Dispatch } from "redux";
import { questionRequest, startQuiz } from "../../actions/index";
import AppBar from "../../components/common/AppBar";
import history from "../../history/history";

const useStyles = makeStyles((theme: Theme) =>
    createStyles({
        root: {
            textAlign: "center",
            margin: theme.spacing(10)
        },
        button: {
            marginTop: theme.spacing(6)
        }

    }));

interface IProps {
    questionRequest: () => void;
    startQuiz: () => void;
}

const QuizHomePage = (props: IProps) => {
    const classes = useStyles();

    const { questionRequest, startQuiz } = props;

    const handleStartQuiz = () => {
        questionRequest();
        startQuiz();
        return history.push("/contentQuiz");
    };
    return (<>
        <AppBar />
        <div className={classes.root}>
            <Typography
                color="textPrimary"
                gutterBottom
                variant="h2">
                Test your javascript skills
            </Typography>
            <Typography
                color="textSecondary"
                gutterBottom
                variant="h6">
                Please click the start button to launch the Quiz
        </Typography>
            <Button
                className={classes.button}
                color="secondary"
                onClick={handleStartQuiz}
                variant="contained">Start</Button>
        </div>
    </>);
};

const mapDispatchToProps = (dispatch: Dispatch) => {
    return {
        startQuiz: () => dispatch(startQuiz()),
        questionRequest: () => dispatch<any>(questionRequest())
    };
};

export default connect(null, mapDispatchToProps)(QuizHomePage);

当我单击Start按钮时,我调度2个动作questionRequest,该动作执行一个Promise,并从数据库中返回所有问题的列表,而startQuiz则调度一个动作以更新状态。测验,然后将用户重定向到此代码描述的测验问题页面:

import { Typography } from "@material-ui/core";
import React from "react";
import { connect } from "react-redux";
import SyntaxHighlighter from "react-syntax-highlighter";
import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";
import { incrementQuestion, IQuestion } from "../../actions/index";
import ContentQuiz from "../../components/ContentQuiz";

interface IProps {
  currentQuestionNumber: number;
  questions: IQuestion[];
}

const QuizzContainer = (props: IProps) => {
  const { currentQuestionNumber, questions } = props;

  const currentQuestion = questions[currentQuestionNumber];
  const handleNextQuiz = () => {
    incrementQuestion();
  };

  return (
    <ContentQuiz
      questionNumber={currentQuestionNumber}
      handleClick={handleNextQuiz}>
      <div>
        <Typography variant="h3" gutterBottom> What's the output of </Typography>
        <>
          <SyntaxHighlighter language="javascript" style={dark}>
            {currentQuestion.questionDescription}
          </SyntaxHighlighter>
        </>

      </div>
    </ContentQuiz>
  );
};

const mapStateToProps = (state: any) => {
  const { currentQuestionNumber, questions } = state.quiz;

  return {
    currentQuestionNumber,
    questions
  };
};

export default connect(mapStateToProps, { incrementQuestion })(QuizzContainer);

actions.ts:

export const questionRequest = (): ThunkAction<void, AppState, null, Action<string>> => {
  return async (dispatch: Dispatch) => {
    dispatch(startQuestionRequest());
    getQuestionsApi().then((response: AxiosResponse) => {
      const { data } = response;
      dispatch(questionSuccess(data.result));
    },
      (error: AxiosError) => {
        let errorMessage = "Internal Server Error";
        if (error.response) {
          errorMessage = error.response.data.error;
        }
        dispatch(questionFail(errorMessage));
        dispatch(errorAlert(errorMessage));
      });
  };
};

我遇到了错误:

TypeError: Cannot read property 'questionDescription' of undefined

通常是因为questions变量未定义。我意识到问题数组不会快速更新,而是由于服务器响应而在一段时间后才更新,这就是QuizzContainer returns the error mentioned below when it tries to mount the component. Is it a good approach to lazy load the component in order to wait the fetching of questions from server and then mounting the QuizContainer component? I tried useEffect which normally behaves as componentDidMount`的原因,但它无法使用我的问题。 我该如何解决?

2 个答案:

答案 0 :(得分:1)

您需要使用异步并在这里等待。如果您不等到诺言解决后再将用户导航到下一页,就永远无法保证用户在页面加载后立即看到问题。

 const handleStartQuiz = async () => {
        awit questionRequest();
        await startQuiz();
        return history.push("/contentQuiz");
    }

第二种方法:(我不建议) 除非您在redux状态下填写了问题,否则请不要提出问题。

return(
     { questions &&  <ContentQuiz> ... </ContentQuiz> }
)

答案 1 :(得分:0)

我使用此更新解决了我的问题:

import { Typography } from "@material-ui/core";
import React from "react";
import { connect } from "react-redux";
import SyntaxHighlighter from "react-syntax-highlighter";
import { dark } from "react-syntax-highlighter/dist/esm/styles/prism";
import { incrementQuestion, IQuestion } from "../../actions/index";
import ContentQuiz from "../../components/ContentQuiz";

interface IProps {
  currentQuestionNumber: number;
  loadingData: boolean;
  questions: IQuestion[];
  questionRequest: () => void;
}

const QuizzContainer = (props: IProps) => {
  const { currentQuestionNumber, loadingData, questions, questionRequest } = props;
  useEffect(() => {
    questionRequest();
  });

  const currentQuestion = questions[currentQuestionNumber];
  const handleNextQuiz = () => {
    incrementQuestion();
  };

  return (
    <div>
   {loadingData ? ("Loading ...") : (
    <ContentQuiz
      questionNumber={currentQuestionNumber}
      handleClick={handleNextQuiz}>
      <div>
        <Typography variant="h3" gutterBottom> What's the output of </Typography>
        <>
          <SyntaxHighlighter language="javascript" style={dark}>
            {currentQuestion.questionDescription}
          </SyntaxHighlighter>
        </>

      </div>
    </ContentQuiz>
)}
</div>
  );
};

const mapStateToProps = (state: any) => {
  const { currentQuestionNumber, loadingData, questions } = state.quiz;

  return {
    currentQuestionNumber,
    loadingData,
    questions
  };
};

const mapDispatchToProps = (dispatch: Dispatch) => {
  return {
    incrementQuestion: () => dispatch(incrementQuestion()),
    questionRequest: () => dispatch<any>(questionRequest())
  };
};

export default connect(mapStateToProps, mapDispatchToProps)(QuizzContainer);