React Class vs Functional Component:当使用钩子和功能组件时,我的其中一项功能会不断重新呈现

亚历山大·卡兰

如果有任何不清楚的地方,请告诉我。

因此,我具有这些功能组件,您可以在答案组件数组中传递这些组件,并使它们以类似于类型形式的测验流呈现。

组件处理了很多逻辑,并将其用于整个应用程序中。

CCFlow一个类组件

import React from "react";
import PropTypes from "prop-types";
import { Row, Col } from "react-bootstrap";

// CC
import CCProgressBar from "../CCProgressBar";
import CCButton from "../CCButton";
import CCFlowAnswer from "../CCFlowAnswer/";

// Local Assets and CSS
import "./CCFlow.css";

class CCFlow extends React.Component {
  constructor(props) {
    super(props);

    const usersAnswers = [];
    props.questions.forEach(() => usersAnswers.push(undefined));

    this.state = {
      currentQuestion: 0,
      usersAnswers: usersAnswers
    };
  }

  // Helpers
  initUsersAnswers = () => {
    const usersAnswers = [];
    this.props.questions.forEach(() => usersAnswers.push(undefined));
    return usersAnswers;
  };

  onLastQuestion = () => {
    return this.state.currentQuestion === this.props.questions.length - 1;
  };

  progress = () => {
    const total = 100 / this.props.questions.length;
    return Math.round(total * (this.state.currentQuestion + 1));
  };

  moveForward = () => {
    this.state.currentQuestion === this.props.questions.length - 1
      ? this.props.handleSubmit(this.state.usersAnswers)
      : this.setState({ currentQuestion: this.state.currentQuestion + 1 });
  };

  cleanAnswers = updatedAnswers => {
    this.props.wipeAnswers[this.state.currentQuestion]
      ? this.setState({
          usersAnswers: updatedAnswers.map((answer, index) =>
            index > this.state.currentQuestion ? undefined : answer
          )
        })
      : this.setState({ usersAnswers: updatedAnswers });
  };

  updateUsersAnswers = (key, answer) => {
    const updatedAnswers = [...this.state.usersAnswers];
    updatedAnswers[key] = answer;
    !this.props.wipeAnswers ||
    !this.props.wipeAnswers[this.state.currentQuestion]
      ? this.setState({ usersAnswers: updatedAnswers })
      : this.cleanAnswers(updatedAnswers);
  };

  handleNextButtonClick = () => {
    this.moveForward();
  };

  manualNextTrigger = () => {
    this.moveForward();
  };

  handleSkip = () => {
    this.updateUsersAnswers(this.state.currentQuestion, "None");
    this.moveForward();
  };

  handleBackButtonClick = () => {
    this.state.currentQuestion !== 0
      ? this.setState({ currentQuestion: this.state.currentQuestion - 1 })
      : window.history.back();
  };

  saveAnswer = (answer, answerKey) => {
    this.updateUsersAnswers(answerKey, answer);
  };

  render() {
    const { questions, style, answers } = this.props;
    const { currentQuestion, usersAnswers } = this.state;

    return (
      <div className="ccQuestions" style={style ? style : {}}>
        <Row>
          <Col xs={3}>
            <h4 style={{ minHeight: "80px" }}>{questions[currentQuestion]} </h4>
            <div id="ccFlowRow">
              <CCProgressBar
                width="200px"
                now={this.progress()}
              ></CCProgressBar>
              <span>{`${this.progress()}%`}</span>
            </div>
            <div id="ccFlowButtons">
              <CCButton variant="dark" onClick={this.handleBackButtonClick}>
                {currentQuestion === 0 ? "Exit" : "Back"}
              </CCButton>
              <CCButton
                style={{ marginLeft: "15px" }}
                variant={this.onLastQuestion() ? "primary" : "info"}
                onClick={this.handleNextButtonClick}
                disabled={usersAnswers[currentQuestion] ? false : true}
              >
                {this.onLastQuestion() ? "Create" : "Next"}
              </CCButton>
            </div>
          </Col>
          <Col xs={9}>
            <CCFlowAnswer
              FlowAnswer={answers[currentQuestion]}
              prevAnswer={
                currentQuestion !== 0 ? usersAnswers[currentQuestion - 1] : null
              }
              allAnswers={usersAnswers}
              handleAnswer={this.saveAnswer}
              questionIndex={currentQuestion}
              answer={
                usersAnswers[currentQuestion]
                  ? usersAnswers[currentQuestion]
                  : null
              }
              handleSkip={this.handleSkip}
              next={this.manualNextTrigger}
            />
          </Col>
        </Row>
      </div>
    );
  }
}

CCFlow.defaultProps = {
  questions: [],
  answers: [],
  wipeAnswers: []
};

CCFlow.propTypes = {
  style: PropTypes.object,
  questions: PropTypes.arrayOf(PropTypes.string),
  answers: PropTypes.arrayOf(PropTypes.elementType),
  handleSubmit: PropTypes.func,
  wipeAnswers: PropTypes.arrayOf(PropTypes.bool)
};

export default CCFlow;

现在作为功能组件,当按如下方式构建saveAnswer函数和updateUsersAnswers时,内容会重新渲染,从而导致CCFlowAnswer重新渲染

import React, { useState } from "react";
import PropTypes from "prop-types";
import { Row, Col } from "react-bootstrap";

// CC
import CCProgressBar from "../CCProgressBar";
import CCButton from "../CCButton";
import CCFlowAnswer from "../CCFlowAnswer/";

// Local Assets and CSS
import "./CCFlow.css";

const CCFlow = ({ style, questions, answers, loaderLogic, handleSubmit }) => {
  // State
  const [currentQuestion, setCurrentQuestion] = useState(0);
  const [usersAnswers, setUsersAnswers] = useState(initUsersAnswers());

  // Helpers
  function initUsersAnswers() {
    const usersAnswers = {};
    questions.forEach((question, index) => {
      usersAnswers[`answer${index + 1}`] = null;
    });
    return usersAnswers;
  }

  function onLastQuestion() {
    return currentQuestion === questions.length - 1;
  }

  function progress() {
    const total = 100 / questions.length;
    return Math.round(total * (currentQuestion + 1));
  }

  function currentAnswerKey() {
    return `answer${currentQuestion + 1}`;
  }

  // Actions
  function handleNextButtonClick() {
    currentQuestion === questions.length - 1
      ? handleSubmit(usersAnswers)
      : setCurrentQuestion(currentQuestion + 1);
  }

  function handleBackButtonClick() {
    currentQuestion !== 0
      ? setCurrentQuestion(currentQuestion - 1)
      : window.history.back();
  }

  function saveAnswer(answer, answerKey) {
    setUsersAnswers({ ...usersAnswers, [answerKey]: answer });
  }

  return (
    <div className="ccQuestions" style={style ? style : {}}>
      <Row>
        <Col xs={3}>
          <h4 style={{ minHeight: "80px" }}>{questions[currentQuestion]} </h4>
          <div id="ccFlowRow">
            <CCProgressBar width="200px" now={progress()}></CCProgressBar>
            <span>{`${progress()}%`}</span>
          </div>
          <div id="ccFlowButtons">
            <CCButton variant="dark" onClick={handleBackButtonClick}>
              {currentQuestion === 0 ? "Exit" : "Back"}
            </CCButton>
            <CCButton
              style={{ marginLeft: "15px" }}
              variant={onLastQuestion() ? "primary" : "info"}
              onClick={handleNextButtonClick}
              disabled={usersAnswers[currentAnswerKey()] ? false : true}
            >
              {onLastQuestion() ? "Create" : "Next"}
            </CCButton>
          </div>
        </Col>
        <Col xs={9}>
          <CCFlowAnswer
            FlowAnswer={answers[currentQuestion]}
            loadBefore={loaderLogic[currentQuestion]}
            handleAnswer={answer =>
              saveAnswer(answer, `answer${currentQuestion + 1}`)
            }
            answer={
              usersAnswers[currentAnswerKey()]
                ? usersAnswers[currentAnswerKey()]
                : null
            }
          />
        </Col>
      </Row>
    </div>
  );
};

CCFlow.defaultProps = {
  questions: [],
  answers: [],
  waitForAnswers: []
};

CCFlow.propTypes = {
  style: PropTypes.object,
  questions: PropTypes.arrayOf(PropTypes.string),
  answers: PropTypes.arrayOf(PropTypes.elementType),
  loaderLogic: PropTypes.arrayOf(PropTypes.any),
  handleSubmit: PropTypes.func,
  waitForAnswers: PropTypes.arrayOf(PropTypes.bool)
};

export default CCFlow;


真是在这里迷路了,因此不胜感激,对于钩子我是陌生的,所以我可能很简单。

亚历山大·卡兰

事实证明,仅将内容包装在useCallback中是行不通的,因为我还有其他问题,例如根据那里的答案在禁用和启用之间进行按钮切换。

我决定重新编写我的组件,使其具有两种状态,一种以单独的状态存储总体答案和当前答案。这样,我可以将保存答案包装在useCallback中,并且只有一个依赖项允许最小程度的重新渲染,而且我的按钮也处于活动/禁用状态。

如果有人感兴趣的话,这是我的全部内容,当涉及到将状态分解成小部分时,我发现需要对钩子进行更多的思考。

import React, { useState, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
import { Row, Col } from "react-bootstrap";

// CC
import CCProgressBar from "../CCProgressBar";
import CCButton from "../CCButton";
import CCFlowAnswer from "../CCFlowAnswer/";

// Local Assets and CSS
import "./CCFlow.css";

const CCFlow = ({ questions, answers, wipeAnswers, handleSubmit, style }) => {
  const [currentQuestion, setCurrentQuestion] = useState(0);
  const [usersAnswers, setUsersAnswers] = useState();
  const [currentAnswer, setCurrentAnswer] = useState(undefined);
  const [wipe, setWipe] = useState(false);

  useEffect(() => {
    setUsersAnswers(questions.map(() => undefined));
  }, [questions]);

  // Helpers
  function onLastQuestion() {
    return currentQuestion === questions.length - 1;
  }

  function progress() {
    const total = 100 / questions.length;
    return Math.round(total * (currentQuestion + 1));
  }

  function loadLastAnswer() {
    setCurrentAnswer(() => usersAnswers[currentQuestion - 1]);
    setCurrentQuestion(currentQuestion - 1);
  }

  function submitAnswers(answer, allAnswers, questionIndex) {
    const submittableAnswers = allAnswers;
    submittableAnswers[questionIndex] = answer;
    handleSubmit(submittableAnswers);
  }

  function cleanAnswers(allAnswers, wipeAnswers, wipe, questionIndex) {
    return wipe && wipeAnswers[questionIndex]
      ? allAnswers.map((answer, index) =>
          index > questionIndex ? undefined : answer
        )
      : allAnswers;
  }

  function loadNextAnswer(
    answer,
    allAnswers,
    wipeOptions,
    clear,
    questionIndex
  ) {
    const updatedUsersAnswers = cleanAnswers(
      allAnswers,
      wipeOptions,
      clear,
      questionIndex
    );
    updatedUsersAnswers[questionIndex] = answer;
    setWipe(false);
    setUsersAnswers(updatedUsersAnswers);
    setCurrentAnswer(
      updatedUsersAnswers[questionIndex + 1]
        ? updatedUsersAnswers[questionIndex + 1]
        : undefined
    );
    setCurrentQuestion(questionIndex + 1);
  }

  // Actions
  function moveForward(skip) {
    const ca = skip ? "None" : currentAnswer;
    currentQuestion === questions.length + 1
      ? submitAnswers(ca, usersAnswers, currentQuestion)
      : loadNextAnswer(ca, usersAnswers, wipeAnswers, wipe, currentQuestion);
  }

  function handleNextButtonClick() {
    moveForward();
  }

  function manualNextTrigger() {
    moveForward();
  }

  function handleSkip() {
    moveForward(true);
  }

  function handleBackButtonClick() {
    currentQuestion !== 0 ? loadLastAnswer() : window.history.back();
  }

  const saveAnswer = useCallback(answer => {
    setCurrentAnswer(answer);
    setWipe(() => true);
  }, []);

  return (
    <div className="ccQuestions" style={style ? style : {}}>
      <Row>
        <Col xs={3}>
          <h4 style={{ minHeight: "80px" }}>{questions[currentQuestion]} </h4>
          <div id="ccFlowRow">
            <CCProgressBar width="200px" now={progress()}></CCProgressBar>
            <span>{`${progress()}%`}</span>
          </div>
          <div id="ccFlowButtons">
            <CCButton variant="dark" onClick={handleBackButtonClick}>
              {currentQuestion === 0 ? "Exit" : "Back"}
            </CCButton>
            <CCButton
              style={{ marginLeft: "15px" }}
              variant={onLastQuestion() ? "primary" : "info"}
              onClick={handleNextButtonClick}
              disabled={currentAnswer ? false : true}
            >
              {onLastQuestion() ? "Create" : "Next"}
            </CCButton>
          </div>
        </Col>
        <Col xs={9}>
          <CCFlowAnswer
            FlowAnswer={answers[currentQuestion]}
            prevAnswer={
              currentQuestion !== 0 ? usersAnswers[currentQuestion - 1] : null
            }
            allAnswers={usersAnswers}
            handleAnswer={saveAnswer}
            answer={currentAnswer}
            handleSkip={handleSkip}
            next={manualNextTrigger}
          />
        </Col>
      </Row>
    </div>
  );
};

CCFlow.defaultProps = {
  questions: [],
  answers: [],
  wipeAnswers: []
};

CCFlow.propTypes = {
  style: PropTypes.object,
  questions: PropTypes.arrayOf(PropTypes.string),
  answers: PropTypes.arrayOf(PropTypes.elementType),
  handleSubmit: PropTypes.func,
  wipeAnswers: PropTypes.arrayOf(PropTypes.bool)
};

export default CCFlow;


本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

Related 相关文章

热门标签

归档