渲染不同组件时无法更新组件

时间:2021-07-27 17:42:21

标签: reactjs

我正在尝试使用 Express 后端在 Reactjs 和 TypeScript 中实现多项选择测验。

Lesson 组件从 API 中获取一个 Lesson 对象,其中包含每个多项选择题的字段,将当前状态设置为课程对象,然后为每个选择题呈现一个 MultipleChoiceQuestion 组件。如果答案正确与否,我将回调函数传递给他们每个人以更新分数。但是,当我调用该函数时,出现此错误:

Warning: Cannot update a component (`Lesson`) while rendering a different component (`MultipleChoiceQuestion`). To locate the bad setState() call inside `MultipleChoiceQuestion`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render
MultipleChoiceQuestion@http://localhost:3000/static/js/main.chunk.js:1715:89
div
div
div
Lesson@http://localhost:3000/static/js/main.chunk.js:1473:108
Route@http://localhost:3000/static/js/vendors~main.chunk.js:34738:29
Switch@http://localhost:3000/static/js/vendors~main.chunk.js:34940:29
Router@http://localhost:3000/static/js/vendors~main.chunk.js:34373:30
BrowserRouter@http://localhost:3000/static/js/vendors~main.chunk.js:33993:35
App
Context@http://localhost:3000/static/js/main.chunk.js:2721:81 index.js:1

这是我的代码,去掉了不相关的部分

课程组件


    import MultipleChoiceQuestion from './MultipleChoiceQuestion'
    
    function Lesson() {
        const [lesson, setLesson] = useState<Lesson>()
        const [score, setScore] = useState(0)
        const [maxScore, setMaxScore] = useState<number>(0)
        const [attempt, setAttempt] = useState(false)
    
        useEffect(() => {
            // fetch from api
        }, [lessonID])
    
        const getQuestionScore = (questionScore: number, questionMax: number) => {
            setScore(score + questionScore)
            setMaxScore(maxScore + questionMax)
        }
        
        return (
            <div>
                        {lesson.questions.map((q: MultipleChoiceQuestion) => {
                            const incorrect = q.incorrect
                            const correct = q.correct
                            return <MultipleChoiceQuestion 
                            attempt={attempt} key={q.question} 
                            question={q.question} scoreCallback={getQuestionScore}
                            correct={correct} incorrect={incorrect} />
                        })}
    
                        <button onClick={() => setAttempt(true)} >Test</button>
                        <h3>{score}/{maxScore}</h3>
    
                    </div>
                </div>
            </div>
        )}

MultipleChoiceQuestion 组件


    function MultipleChoiceQuestion(props: {
        correct: string[],
        attempt: any, incorrect: string[],
        question: string, scoreCallback: any
    }) {
        const [hasReturnedScore, setHasReturnedScore] = useState(false)
    
        const handleChange = (e: any) => {
            setSelected(e.target.value)
        }
    
        
        if (props.attempt && !hasReturnedScore) {
            setHasReturnedScore(true)
            const score = calculateScore()
            score ? props.scoreCallback(1, 1) : props.scoreCallback(0, 1)
        }
    
        return (
            <div className="container">
                        <h2 className="title is-6">{props.question}</h2>
                                {props.correct.map((choice: any) => {
                                    return <AnswerBox key={choice} choice={choice} selected={selected} changeCallback={handleChange} />
                                })}
                                {props.incorrect.map((choice: any) => {
                                    return <AnswerBox key={choice} choice={choice} selected={selected} changeCallback={handleChange} />
                                })}
        )
    }
    
    const AnswerBox = (prop: any) => {
        return (
            <label htmlFor="question" className="column radio" >
                <input
                    id={prop.choice}
                    type="radio"
                    value={prop.choice}
                    checked={prop.selected === prop.choice}
                    onChange={prop.changeCallback}
                    name="choice" />
                {prop.choice}
            </label>
        )}

我错误地认为将数据从子组件传递给父组件是一种聪明的方法,但现在我不知道如何解决这个问题。

1 个答案:

答案 0 :(得分:1)

你的这部分

if (props.attempt && !hasReturnedScore) {
            setHasReturnedScore(true)
            const score = calculateScore()
            score ? props.scoreCallback(1, 1) : props.scoreCallback(0, 1)
        }

目前,它是写在子组件(MultipleChoiceQuestion)的渲染上的。这是错误的,因为它仅在渲染时调用函数。 您需要在某个事件处理程序(一个在单击单选按钮等事件时调用的函数)中移动它,或者您将这个函数移动到 useEffect 钩子内。