如果有任何不清楚的地方,请告诉我。
因此,我具有这些功能组件,您可以在答案组件数组中传递这些组件,并使它们以类似于类型形式的测验流呈现。
组件处理了很多逻辑,并将其用于整个应用程序中。
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] 删除。
我来说两句