React功能组件如何以及为何重新渲染?

时间:2020-05-19 18:54:58

标签: reactjs react-hooks render setstate

休息了几周后,我做了一个React应用程序来测试我的图书馆技能。 The project is on Github,我想了解React的行为:我的某些功能组件多次重传,但我不明白为什么。

例如,我负责当前级别显示的功能组件渲染两次。为什么?

import React, {useState, useEffect} from 'react';
import Stepper from './Stepper';
import style from './style';
const Levels = ({
  levelNames,
  currentLevel
}) => {
  const [steps, setSteps] = useState([]);

  useEffect(
    () => {
      const output = levelNames.map(
        level => ({title: level.toUpperCase()})
      );
      setSteps(output);
    }, [levelNames]
  );  

  return (
    <section className="levelsContainer" style={style.levels}>
      <Stepper stepsList={steps} activeStep={currentLevel}/>
      {console.log(Date.now(), "levels")}
    </section>
  );
}

export default React.memo(Levels);

以同样的方式,每次更新状态时,大一类都会多次放弃。对于这种情况,我想问题是调用setState时某些状态会按值更新值,对吗?我相信setState不能同步工作,这是原因吗?我们该如何解决?

import React, { PureComponent } from 'react';

import Levels from '../Levels';
import ProgressBar from '../ProgressBar';
import Question from '../Question';
import QuizOver from '../QuizOver';

import push from '../../services/push';
import quizData from '../../assets/quizData';

import { initialState, levelNames } from '../../assets/config/gameManager';

class Quiz extends PureComponent {
  constructor(props) {
    super(props);
    this.state = initialState;
    this.storedDataRef = React.createRef();
  }

  getFinalScore = (
    goodAnswersNb,
    maxQuestions
  ) => goodAnswersNb / maxQuestions * 100;

  loadLevel = (incrementLevel) => {
    const newQuizLevel = incrementLevel ? this.state.currentQuizLevel + 1 : 0;
    this.loadQuestions(newQuizLevel);
  };

  loadQuestions = level => {
    const currentQuiz = quizData.quizz[levelNames[level]];
    this.storedDataRef.current = currentQuiz;

    if (currentQuiz.length >= this.state.maxQuestions) {
      const questions = currentQuiz.map(({answer, ...rest}) => rest);
      this.setState({
        ...initialState,
        currentQuizLevel: level,
        storedQuestions: questions,
        currentQuestion: questions[0].question,
        questionOptions: questions[0].options
      });
    }
  };

  handleOptions = selectedAnswer => {
    this.setState({
      isButtonDisabled: false,
      userAnswer: selectedAnswer
    });
  };

  submitAnswer = () => {
    // compute new user score first by comparison with user current answer & good one
    const goodAnswer = this.storedDataRef.current[this.state.questionId].answer;

    const currentScore = this.state.userAnswer === goodAnswer 
    ? this.state.succeeds + 1
    : this.state.succeeds;

    // prepare quiz end data if it is last question or update questions data
    const outputData = {};
    const currentQuestionId = this.state.questionId;

    if(currentQuestionId === this.state.maxQuestions - 1) {
      outputData.score = this.getFinalScore(
        currentScore,
        this.state.maxQuestions
      );
      outputData.isQuizOver = true;
    }
    else {
      outputData.currentQuestion = this.state.storedQuestions[currentQuestionId +1].question;
      outputData.questionOptions = this.state.storedQuestions[currentQuestionId +1].options;
      outputData.userAnswer = null;
      outputData.isButtonDisabled = true;
    }

    // update state with computed data
    this.setState(prevState => ({
      succeeds: currentScore,
      questionId: prevState.questionId + 1,
      ...outputData
    }));

  };

  componentDidMount() {
    this.loadQuestions(this.state.currentQuizLevel);
    push.welcome(this.props.userData.pseudo);
  };

  componentDidUpdate(prevProps, prevState) {
    const {
      questionId,
      succeeds,
    } = this.state;

    // success/failure pop up handlers
    if(questionId === prevState.questionId +1 ) {
      succeeds > prevState.succeeds
      ? push.success() 
      : push.failure();
    }
  };

  render() {
    const {
      currentQuizLevel,
      maxQuestions,
      currentQuestion,
      questionOptions,
      questionId,
      isButtonDisabled,
      userAnswer,
      succeeds,
      isQuizOver,
      score,
      minScore,
    } = this.state;

    return isQuizOver ? (
      <QuizOver
        ref={this.storedDataRef}
        succeeds={succeeds}
        score={score}
        maxQuestions={maxQuestions}
        isLastLevel={currentQuizLevel === levelNames.length -1}
        loadLevel={this.loadLevel}
        isSucceeded={score >= minScore}
      />
    ):(
      <React.Fragment>
        <Levels 
          levelNames={levelNames}
          currentLevel={currentQuizLevel}
        />

        <ProgressBar
          currentQuestionNb={questionId + 1}
          maxQuestions={maxQuestions}
        />
        <Question 
          label={currentQuestion}
          options={questionOptions}
          isButtonDisabled={isButtonDisabled}
          userAnswer={userAnswer}
          handleOptions={this.handleOptions}
          submitAnswer={this.submitAnswer}
        />
      </React.Fragment>
    );
  }
};

export default Quiz;

是一样的吗?因为我正在使用useState和useEffect挂钩,所以它会重新渲染?

要检查组件是否重新渲染,请使用console.log渲染。这样合适吗?有更好的方法吗?

对于那些将检查整个项目的核心人士,我发布了它以获得有关我的代码风格的反馈并吸收了最佳实践,并学习了优化React应用的最佳方法,所以请放心给我你的! =)

0 个答案:

没有答案