You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 05:54:19