我正在学习reactjs并制作测验应用程序。我想使用useEffect在父组件中执行一段代码。useEffect函数的依赖关系基于父组件的状态,该状态将在子组件中更改。我已将set方法发送给子组件。根据我的理解,useEffect函数应该在父组件中工作,但是没有发生。我究竟做错了什么?
我正在向子组件发送答案和setAnswers,并尝试在子组件中调用setAnswers时登录更新了答案的父组件。
父组件:
import React, { useState, useEffect, useRef } from 'react'
import axios from 'axios'
import { baseURL } from '../assets/axiosInstance'
import Row from 'react-bootstrap/Row'
import QuizCard from './QuizCard'
import Spinner from 'react-bootstrap/Spinner'
import Button from 'react-bootstrap/Button'
export default function QuizFetch(props) {
const quiz_id = props.quiz_id
const quiz_uri = props.quiz_uri
const [quiz, setQuiz] = useState()
const [answers, setAnswers] = useState([])
const [correctAnswers, setCorrectAnswers] = useState([])
const [my_error, setError] = useState({has_error: false, message: 0})
const [apiCalling, setApiCalling] = useState(true)
const [ansCounter, setAnsCounter] = useState(0)
useEffect(()=>{
console.log('answers changed')
},[answers])
const first_data_effect = useRef(true)
useEffect(() => {
if(first_data_effect.current){
first_data_effect.current = false
return
}
let ans = []
let q_len = quiz.questions.length
for(let c = 0; c < q_len; c++){
let questions = quiz.questions[c]
let a_len = questions.options.length;
for (let d = 0; d < a_len; d++){
if (questions.options[d].correct_answer === 'yes'){
ans[`question_${questions.id}`] = questions.options[d].id
}
}
}
console.log('ans',ans)
setCorrectAnswers(ans)
console.log('corr ans',correctAnswers)
setAnsCounter(quiz.questions.length)
console.log('qlen',quiz.questions.length)
}, [quiz])
useEffect(() => {
axios.post(baseURL + 'quiz/get-quiz', {
quiz_id: quiz_id,
quiz_uri: quiz_uri
}, {
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('scs_token')}`
},
})
.then((response) => {
const resp = response.data
setQuiz(resp[0])
})
.catch((error) => {
console.log("inside error")
setError({
has_error: true,
message: error.response.data['message']
})
})
.then(() => {
setApiCalling(false)
})
}, [])
if (apiCalling) {
console.log('api calling true')
return (
<>
<div style={{ height: "200px", display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Spinner animation="grow" variant="primary" />
</div>
</>
)
} else if (my_error['has_error']) {
console.log('error')
return (
<>
<h1>Quiz: {props.quiz_uri}</h1>
{my_error['message']}
</>
)
} else {
return (
<>
<div>
<h1 style={{ textAlign: 'center'}}>
{quiz.quiz_title}
</h1>
Yet to Answer: {ansCounter}
Answered: {answers.length}
<Row style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
{quiz.questions.map(q => (
<QuizCard key={q.id} quizid={quiz.id} question={q} answers={answers} setAnswers={setAnswers}/>
))}
</Row>
<br />
</div>
<br /><br />
</>
)
}
}
子组件:
import React, {useState, useEffect} from 'react'
import Col from 'react-bootstrap/Col'
import axios from 'axios'
import { baseURL } from '../assets/axiosInstance'
import Spinner from 'react-bootstrap/Spinner'
export default function QuizCard(props){
const [question, setQuestion] = useState(props.question)
const [showCorrect, setShowCorrect] = useState(false)
// const [apiCalling, setApiCalling] = useState(false)
const onChangeValue = (e) =>{
setShowCorrect(true)
let result = props.answers
result[e.target.name] = e.target.value
props.setAnswers(result)
console.log('on change answers',props.answers)
// setApiCalling(true)
axios.post(baseURL + 'quiz/record-each-answer', {
quiz_id: props.quizid,
question_id: e.target.name,
option_id: e.target.value
}, {
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('scs_token')}`
},
})
.then((response) => {
console.log('successful')
})
.catch((error) => {
console.log('failed')
})
}
return(
<>
<Col md={3} xs={8} style={{margin:"5px",padding:"15px 10px", backgroundColor:"#343a40", borderRadius:'15px', color:"white", position:"relative",top:'0px'}}>
<h5 style={{marginLeft:"10px"}}>{question.question}</h5>
{/* {apiCalling ? <Spinner animation="border" variant="primary" size="sm" /> : ""} */}
<div style={{marginLeft:"10px"}}>
{question.options.map(option=>(
<div key={option.id}>
<input
type="radio"
disabled={showCorrect}
{...props.answers[`question_${question.id}`] === option.id ? "checked" : ""}
value={option.id}
onClick={(e)=>onChangeValue(e)}
name={`question_${question.id}`}/>
<span style={{width:'100%', backgroundColor:((showCorrect && option.correct_answer) === "yes" ? "green": "")}} >{option.answer}</span></div>
))}
</div>
</Col>
</>
)
}
问题在于您正在更改状态,而不是创建包含新状态的新对象。您永远都不应改变状态。React通过身份比较状态,因为您只是向同一对象添加属性,所以React认为状态没有改变,因为它仍然是同一对象。您需要创建一个新对象,并将旧状态与新答案合并:
const onChangeValue = (e) => {
// ...
props.setAnswers(currentAnswers => ({...currentAnswers, [e.target.name]: e.target.value}));
// ...
}
请注意,这是如何使用的回调版本的setAnswers
。它接受一个以当前状态为参数的函数,并应返回新状态。当您要基于当前状态更新状态时,应始终使用该名称。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句