When trying to create a simple quiz app without the need to prop drilling I've stumbled upon an issue while trying to integrate context into the project. The issue is that when subscribing to my context as shown below and console. logging 'name', I get the value of undefined. What am I missing in order to get my name(stored in a state in my context) logged instead of getting undefined?
My context
import React, { createContext, Dispatch, SetStateAction, useContext, useState } from 'react';
export interface IUserContextType {
name: string;
test: string;
setName: Dispatch<SetStateAction<string>>;
setTest: Dispatch<SetStateAction<string>>;
}
type IQuizContextProvidorProps = {
children: React.ReactNode;
};
export const QuizContext = createContext({} as IUserContextType);
export const useQuizContext = () => useContext(QuizContext);
const QuizContexProvider = ({ children }: IQuizContextProvidorProps) => {
const [name, setName] = useState('Marvin');
const [test, setTest] = useState('This is a test');
const values = { name, test, setName, setTest };
return <QuizContext.Provider value={values}>{children}</QuizContext.Provider>;
};
export default QuizContexProvider;
My App
import { useState } from 'react';
import './App.css';
import quizApi from './utils/quiz.json';
import { IQuiz, IQuizAnswers } from './model/IQuiz';
import { Button, LinearProgress, Paper, styled, Typography } from '@mui/material';
import { Box } from '@mui/system';
import QuizContexProvider, { useQuizContext } from './utils/QuizContex';
const QuizContainer = styled(Box)(({ theme }) => ({
'.correct': {
backgroundColor: 'darkseagreen',
},
'.linearProgress': {
height: '1rem',
},
}));
function App() {
const { name, test } = useQuizContext();
console.log('name', name);
function shuffle(array: Array<any>) {
return array.sort(() => Math.random() - 0.5);
}
const quiz: Array<IQuiz> = shuffle(quizApi);
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const [progress, setProgress] = useState(0);
const [viewQuiz, setViewQuiz] = useState(true);
const [quizScore, setQuizScore] = useState(0);
const inkrementWith = 100 / quiz.length;
const handleProgress = () => {
setProgress(progress inkrementWith);
};
const handleAnswer = (answers: IQuizAnswers) => {
const nextQuestion = currentQuestionIndex 1;
handleProgress();
if (nextQuestion < quiz.length) {
setCurrentQuestionIndex(nextQuestion);
} else {
setViewQuiz(false);
}
if (answers.isTrue === true) {
setQuizScore(quizScore 1);
}
};
const handleReset = () => {
setCurrentQuestionIndex(0);
setProgress(0);
setQuizScore(0);
setViewQuiz(true);
};
return (
<QuizContexProvider>
<QuizContainer className='App'>
<Box component='header' className='App-header'>
{viewQuiz ? (
<>
<Box sx={{ width: '50%' }}>
<LinearProgress className='linearProgress' variant='determinate' color='success' value={progress} />
</Box>
{quiz.map(
(question, index) =>
index === currentQuestionIndex && (
<Box key={index}>
<Box>{question.questionLabel}</Box>
<Box sx={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem', margin: '1rem' }}>
{shuffle(question.answerOptions).map((answers, index) => (
<Paper
key={index}
onClick={() => {
return handleAnswer(answers);
}}
component='button'
>
{answers.answerLabel}
</Paper>
))}
</Box>
</Box>
)
)}
</>
) : (
<Paper>
<Typography component='h1' variant='h3'>
Quiz results
</Typography>
<Typography component='h2' variant='subtitle1'>
Quiz results
</Typography>
<Typography component='h1' variant='h1' sx={{ fontWeight: 700 }}>
{quizScore} / {quiz.length}
</Typography>
<Button variant='contained' onClick={handleReset} sx={{ margin: '1rem 0rem' }}>
Reset quiz
</Button>
</Paper>
)}
</Box>
</QuizContainer>
</QuizContexProvider>
);
}
export default App;
CodePudding user response:
Any component that wish to use context value, should be wrapped inside the provider. Your <App />
component is using context value, so it should be:
<QuizContexProvider>
<App />
</QuizContexProvider>
You can put the provider in Index.ts
file.