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

React类组件中this.context未定义错误的解决方法

问题描述

我需要实现两个功能:

  • 保护部分路由,仅允许已认证用户访问(该功能已实现);
  • 根据用户是否已认证展示不同的HTML内容(如登录表单或欢迎消息)。

我按照教程使用React Context实现该功能,但运行时在UserGreeting函数中出现以下错误:

undefined is not an object (evaluating 'this.context')

相关代码如下:

src/context/AuthContext.tsx

import { createContext } from "react"

interface AuthContext {
    isAuth: boolean
    isLoading: boolean
}

export const AuthContext = createContext<AuthContext>({
    isAuth: false,
    isLoading: true,
})

src/hooks/useAuth.ts

import { useEffect, useState } from "react"
import isAuthenticated from "../utils/auth"

export const useAuth = () => {
    const [isAuth, setIsAuth] = useState(false) 
    const [isLoading, setIsLoading] = useState(true) 

    useEffect(() => {
        const checkAuth = async() => {
            if (await isAuthenticated()) {
                console.log("Authenticated true")
                setIsAuth(true)
            } else {
                console.log("Authenticated false")
                setIsAuth(false)
            }

            setIsLoading(false)
        }

        checkAuth()
    }, [])

    return { isAuth, isLoading }
}

src/App.tsx

import AppRouter from './routes/Routes'
import "./styles/App.sass"
import { useAuth } from "./hooks/useAuth"
import { AuthContext } from "./context/AuthContext"
import Header from "./components/Header/Header"

function App() {
    const { isAuth, isLoading } = useAuth()
    return (
        <AuthContext.Provider value={{ isAuth, isLoading }}>
            <div className="App">
                <Header />
                
                <main className="App-content">
                    <AppRouter />
                </main>
            </div>
        </AuthContext.Provider>
    )
}

export default App

src/routes/Routes.tsx

import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'
import React, { useState, useEffect, useContext, createContext } from "react"
import { useAuth } from "../hooks/useAuth"
import Home from "../views/Home"
import Panel from "../views/Panel"

const ProtectedRoute = ({Component} : { Component: React.ComponentType }) => {
    
    const { isAuth, isLoading } = useAuth()

    if (isLoading) {
        console.log("Is loading")
        return null
    }

    console.log("isAuthenticated? " + isAuth)

    if (!isAuth) {
        return <Navigate to="/" replace />
    }

    return <Component />
}

const AppRouter = () => {
    return (
            <Router>
                <Routes>
                    <Route path="/" element={<Home />} />
                    <Route path="/panel" element={<ProtectedRoute Component={Panel} />} />
                </Routes>
            </Router>
    )
}

export default AppRouter

src/views/Home.tsx

import React, { Component, useContext } from 'react'
import { AuthContext } from "../context/AuthContext"
import axios from "axios"
import Cookies from "js-cookie"
import "../styles/Home.sass"

interface HomeProps {}

interface HomeState {
    message: Record<string, string>
    inputs: Record<string, string>
}

class Home extends Component<HomeProps, HomeState> {

    static contextType = AuthContext
    context!: React.ContextType<typeof AuthContext>
        
    constructor(props: HomeProps, context: React.ContextType<typeof AuthContext>) {
        super(props, context)
        
        this.state = {
            message: {},
            inputs: {},
        }

        this.handleChange = this.handleChange.bind(this)
        this.Form = this.Form.bind(this)
    }

    componentDidMount() {

    }

    handleChange : React.ChangeEventHandler<HTMLInputElement> = (event) => {
        let inputs = this.state.inputs
        inputs[event.target.name] = event.target.value
        this.setState({inputs})
    }

    render() {
        return (
            <div id="container">
                <div id="text">
                    <div>
                        <h1>Welcome to Finonline</h1>
                        <span id="slogan">Administrate your personal expenses online.</span>
                        <p>
                            This is a demo-project for my job-application showcasing my skills in full-stack webdevelopment, Java, Spring Boot and React.js.
                        </p>
                    </div>
                </div>
                <div id="form-container">
                    <this.UserGreeting />
                </div>
            </div>
        )
    }

    UserGreeting() {
        const {isAuth} = this.context

        if (isAuth) {
            return (
                <div id="welcome">
                    <span className="title">Welcome </span>
                    <a href="/panel" className="button">Go to panel.</a>
                </div>
            )
        } else {
            return <this.Form />
        }
    }

    Form() {
        const handleSubmit = (event: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLInputElement, MouseEvent>, action: string) => {
            event.preventDefault()
            
            switch (action) {
                case "register":
                    this.register(this.state.inputs)
                    break
                default:
                    this.login(this.state.inputs)
            }
        }

        return (
            <form onSubmit={(event) => handleSubmit(event, 'login')} >
                <span className="title">Login</span>
                <div className="input-wrapper">
                    <img src="/assets/images/profile.png" alt="" />
                    <input type="text" name="name" placeholder="Username" minLength={3} required={true} onChange={this.handleChange} />
                </div>
                <div className="input-wrapper">
                    <img src="/assets/images/password.png" alt="" />
                    <input type="password" name="pass" placeholder="Password" minLength={5} required={true} onChange={this.handleChange} />
                </div>
                <div id="form-buttons">
                    <input type="submit" name="login" value="Login" onClick={(event) => handleSubmit(event, 'login')} />
                    <input type="submit" name="register" value="Or register" id="registerbtn" onClick={(event) => handleSubmit(event, 'register')} />
                </div>
                <span id="message" className={this.state.message["type"]}>
                    { this.state.message["text"] }
                </span>
            </form>
        )
    }

    async register(data: Record<string, string>) {
        let message = this.state.message

        try {
            const response = await axios.post(`http://127.0.0.1:8080/users`,
                    data
                ,
                {
                    insecureHTTPParser: true,
                    headers: {
                        "Content-Type": "application/json",
                    }
                })

            if (response.status == 201) {
                message["type"] = "ok"
                message["text"] = "Successfully registered."
                
                this.setState({ message  })
            } else {
                message["type"] = "error"
                message["text"] = "Error: " + response.data
                
                this.setState({ message  })
            }
        } catch (err) {
            message["type"] = "error"
            message["text"] = "Error: This user already exists."
            
            this.setState({ message  })

        }
    }

    async login(data: Record<string, string>) {
       let message = this.state.message

        try {
           const response = await axios.post(`http://127.0.0.1:8080/auth`,
                    data
                ,
                {
                    insecureHTTPParser: true,
                    headers: {
                        "Content-Type": "application/json",
                    }
                })
            if (response.status == 200) {
                message["type"] = "ok"
                message["text"] = "Login successful."
                
                this.setState({ message  })
                
                const token = response.data.token
                Cookies.set("auth-token", token, { expires: 1 })

            } else {
                message["type"] = "error"
                message["text"] = "Error: Login failed."
                
                this.setState({ message  })
            }
        } catch (err) {
            message["type"] = "error"
            message["text"] = "Error: Login failed."
           
            console.log(err)

            this.setState({ message  })
        }
    }
}

export default Home

解决方案

问题根源

UserGreeting是类内部定义的普通函数,并非类的方法,调用时this的指向丢失,导致this.context为undefined。同时Form函数也存在相同的this指向问题,需要一并修复。

修复方式

方式1:绑定this到类方法

在构造函数中为UserGreeting和Form绑定this:

constructor(props: HomeProps, context: React.ContextType<typeof AuthContext>) {
    super(props, context)
    
    this.state = {
        message: {},
        inputs: {},
    }

    this.handleChange = this.handleChange.bind(this)
    this.Form = this.Form.bind(this)
    this.UserGreeting = this.UserGreeting.bind(this) // 新增绑定
}

方式2:使用箭头函数定义方法

箭头函数会自动绑定当前类的this,无需手动绑定:

UserGreeting = () => {
    const {isAuth} = this.context

    if (isAuth) {
        return (
            <div id="welcome">
                <span className="title">Welcome </span>
                <a href="/panel" className="button">Go to panel.</a>
            </div>
        )
    } else {
        return <this.Form />
    }
}

Form = () => {
    const handleSubmit = (event: React.FormEvent<HTMLFormElement> | React.MouseEvent<HTMLInputElement, MouseEvent>, action: string) => {
        event.preventDefault()
        
        switch (action) {
            case "register":
                this.register(this.state.inputs)
                break
            default:
                this.login(this.state.inputs)
        }
    }

    return (
        <form onSubmit={(event) => handleSubmit(event, 'login')} >
            <span className="title">Login</span>
            <div className="input-wrapper">
                <img src="/assets/images/profile.png" alt="" />
                <input type="text" name="name" placeholder="Username" minLength={3} required={true} onChange={this.handleChange} />
            </div>
            <div className="input-wrapper">
                <img src="/assets/images/password.png" alt="" />
                <input type="password" name="pass" placeholder="Password" minLength={5} required={true} onChange={this.handleChange} />
            </div>
            <div id="form-buttons">
                <input type="submit" name="login" value="Login" onClick={(event) => handleSubmit(event, 'login')} />
                <input type="submit" name="register" value="Or register" id="registerbtn" onClick={(event) => handleSubmit(event, 'register')} />
            </div>
            <span id="message" className={this.state.message["type"]}>
                { this.state.message["text"] }
            </span>
        </form>
    )
}

两种方式任选其一即可解决this.context undefined的问题,同时也能避免Form函数中可能出现的this指向错误。


内容的提问来源于stack exchange,提问作者O'Niel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 02:45:58