TypeScript+React订阅Context控制台输出undefined问题求助
React Context 获取状态为undefined的问题修复
问题描述
我正在开发一款无需进行props drilling的简易Quiz应用,在集成React Context时遇到问题:按照下述代码订阅Context后,控制台打印的name值为undefined。请问需要修改哪些部分才能正确获取Context中存储的name状态?
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;
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;
问题分析
App组件在自身函数体内调用useQuizContext,但QuizContexProvider是在App的return语句中才渲染,导致App组件本身不在Context的作用域范围内,因此无法获取到Context中的值。
修复方案
- 将
useQuizContext的调用移到Provider包裹的子组件中:把App内的Quiz展示、结果统计等逻辑拆分成独立子组件,在这些子组件内部调用useQuizContext - 将
QuizContexProvider提升到App组件的外层:修改index.tsx,用QuizContexProvider包裹App组件,示例代码如下:
// index.tsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import QuizContexProvider from './utils/QuizContex'; const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement ); root.render( <React.StrictMode> <QuizContexProvider> <App /> </QuizContexProvider> </React.StrictMode> );
内容的提问来源于stack exchange,提问作者FrontEndMarv
相关产品推荐
相关产品推荐

