I am using useContext hook for the first time as I wanted the re-rendering of one component by click of a button component. Here's my code:
QuestionContext.js (for creating context):
import { createContext } from "react";
const QuestionContext = createContext()
export default QuestionContext
SectionState.js (for providing value to children):
import {React, useState} from 'react'
import QuestionContext from './QuestionContext'
import questions from '../data/questions.json'
const SectionState = (props) => {
// set questions from json to an array of 4 elements
const newQuestions = Object.keys(questions.content).map(key => questions.content[key].question)
const newState = {
"qID": 0,
"questionTxt": newQuestions[0],
}
//useState for Question state
const [currentQuestion, setCurrentQuestion] = useState(0)
const [questionCtx, setQuestionCtx] = useState(newState)
const updateQuestion = () => {
if(currentQuestion > newQuestions.length) {
console.log("no more questions")
}
else{
setCurrentQuestion(currentQuestion 1)
setQuestionCtx(() => ({
"qID": currentQuestion,
"questionTxt": newQuestions[currentQuestion]
}))
}
}
return (
<QuestionContext.Provider value = {{newState, updateQuestion}}>
{props.children}
</QuestionContext.Provider>
)
}
export default SectionState
The following two components are child of <SectionState />
component
Buttons.js:
import React, { useContext } from 'react'
import QuestionContext from '../context/QuestionContext'
const Buttons = () => {
const example = useContext(QuestionContext)
const clickHandler = () => {
example.updateQuestion()
}
return (
<div className='flex flex-row justify-between'>
{/* <button className='btn backdrop-blur-md bg-slate-600 rounded-full xl:w-48 md:w-44 text-slate-50' onClick={ clickHandler }>Prev</button> */}
<button className='btn btn-accent rounded-full xl:w-48 md:w-44' onClick={ clickHandler }>Next</button>
</div>
)
}
export default Buttons
Questions.js
import { React, useContext } from 'react'
import './styles/Questions.css'
import QuestionContext from '../context/QuestionContext'
const Questions = () => {
const newContext = useContext(QuestionContext)
return (
<>
<h1 className='text-4xl text-zinc-50'>{ newContext.newState.questionTxt }</h1>
</>
)
}
export default Questions
Every time I have clicked on the button, I could check in the console that newState
state has changed, but this new state won't render in <Questions />
component. I could still see newContext.newState.questionTxt
holding the initial value i.e. newQuestions[0]
. What am I doing wrong here?
Here's a reproduced link in code sandbox
CodePudding user response:
<QuestionContext.Provider value = {{newState, updateQuestion}}
Here you passed newState and updateQuestion as a value of context. In Button component you update currentQuestion and questionCtx using updateQuestion() but in Questions component, you are using the value of newState as
const newContext = useContext(QuestionContext)
<h1 className='text-4xl text-zinc-50'>{ newContext.newState.questionTxt }</h1>
Here newState is not a state. It is just a variable and it is not updated at all so you don't get an updated value in Question component.
Solution: So I think you should pass the questionCtx as a value of context Provider like
<QuestionContext.Provider value = {{questionCtx , updateQuestion}}
Use it like
<h1 className='text-4xl text-zinc-50'>{ newContext.questionCtx.questionTxt }</h1>
Working Codesandbox link: https://codesandbox.io/s/react-usecontext-forked-frgtw1?file=/src/context/SectionState.js