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

使用useReducer+Context管理登录组件状态,输入框无响应问题求助

输入框无响应:useContext + useReducer 管理登录状态异常

我在createContext文件中引入useReducer,希望通过useContext管理所有用户输入,验证输入后更新状态,但登录组件的输入框对任何按键操作都无响应。

reducer-context.js 代码

import React, { useReducer } from "react";

// 上下文定义
const ValContext = React.createContext({
  email: "",
  eValid: null,
  onChangeEmail: (e) => {},
  onEmailIsValid: () => {},
  password: "",
  pValid: null,
  onChangePassword: (e) => {},
  onPasswordIsValid: () => {},
});

// 初始状态
export const INITIAL_STATE = {
  email: "",
  eValid: null,
  password: "",
  pValid: null,
};

// reducer函数
const reducer = (state = INITIAL_STATE, actions) => {
  switch (actions.type) {
    case "EMAIL_INPUT":
      return {
        ...state,
        email: actions.payload,
        eValid: actions.payload.trim().includes("@"),
      };
    case "EMAIL_BLUR":
      return {
        ...state,
        email: state.email,
        eValid: state.email.trim().includes("@"),
      };
    case "PASSWORD_INPUT":
      return {
        ...state,
        password: actions.payload,
        pValid: actions.payload.trim().length >= 6,
      };
    case "PASSWORD_BLUR":
      return {
        ...state,
        password: state.password,
        pValid: state.password.trim().length >= 6,
      };

    default:
      return state;
  }
};

// 上下文Provider组件
export const Validation = (props) => {
  const [state, dispatch] = useReducer(reducer, {
    email: "",
    eValid: null,
    password: "",
    pValid: null,
  });
  console.log(state);
  const emailChangeHandler = (email) => {
    dispatch({ type: "EMAIL_INPUT", payload: email });
  };
  const passwordChangeHandler = (e) => {
    dispatch({ type: "PASSWORD_INPUT", payload: e.target.value });
  };
  const emailValidation = (e) => {
    dispatch({ type: "EMAIL_BLUR" });
  };
  const passwordValidation = (e) => {
    dispatch({ type: "PASSWORD_BLUR" });
  };
  return (
    <ValContext.Provider
      value={{
        email: state.email,
        eValid: state.eValid,
        onChangeEmail: (e) => emailChangeHandler(e),
        onEmailIsValid: emailValidation,
        password: state.password,
        pValid: state.pValid,
        onChangePassword: passwordChangeHandler,
        onPasswordIsValid: passwordValidation,
      }}
    >
      {props.children}
    </ValContext.Provider>
  );
};

export default ValContext;

Login.js 代码

import React, { useContext, useState, useEffect } from "react";

import Card from "../UI/Card/Card";
import classes from "./Login.module.css";
import Button from "../UI/Button/Button";
import AuthContext from "../../store/auth-context";
import ValContext, { Validation } from "../../store/reducer-context";

const Login = (props) => {
  const [formIsValid, setFormIsValid] = useState(false);
  const authCtx = useContext(AuthContext);

  // 获取状态上下文
  const valCtx = useContext(ValContext);

  useEffect(() => {
    setFormIsValid(valCtx.eValid && valCtx.pValid);
  }, [valCtx.eValid, valCtx.pValid]);

  const submitHandler = (event) => {
    event.preventDefault();
    authCtx.onLogin(valCtx.email, valCtx.password);
  };

  return (
    <Validation>
      <Card className={classes.login}>
        <form onSubmit={submitHandler}>
          <div
            className={`${classes.control} ${
              valCtx.eValid === false ? classes.invalid : ""
            }`}
          >
            <label htmlFor="email">E-Mail</label>
            <input
              type="email"
              id="email"
              value={valCtx.email}
              onChange={valCtx.onChangeEmail}
              onBlur={valCtx.onEmailIsValid}
            />
          </div>
          <div
            className={`${classes.control} ${
              valCtx.pValid === false ? classes.invalid : ""
            }`}
          >
            <label htmlFor="password">Password</label>
            <input
              type="password"
              id="password"
              value={valCtx.password}
              onChange={valCtx.onChangePassword}
              onBlur={valCtx.onPasswordIsValid}
            />
          </div>
          <div className={classes.actions}>
            <Button
              type="submit"
              className={classes.btn}
              disabled={!formIsValid}
            >
              Login
            </Button>
          </div>
        </form>
      </Card>
    </Validation>
  );
};

export default Login;

问题原因及修复方案

1. 上下文Provider嵌套错误

Login组件内部直接包裹了<Validation>(上下文Provider),导致:

  • 每次Login组件渲染时,都会创建新的Provider实例,上下文状态被重复初始化
  • useContext(ValContext)获取的是外层默认上下文(不是内部Provider提供的状态),输入框绑定的是默认值,自然无法响应更新

修复:
将<Validation>移到Login组件的外层(比如App.js中),确保上下文作用域覆盖Login组件:

// App.js示例
import Login from './components/Login';
import { Validation } from './store/reducer-context';

function App() {
  return (
    <Validation>
      <Login />
    </Validation>
  );
}

同时修改Login组件,移除内部的<Validation>包裹:

// Login.js中修改return部分
return (
  <Card className={classes.login}>
    <form onSubmit={submitHandler}>
      {/* 原有表单内容保持不变 */}
    </form>
  </Card>
);

2. 邮箱变更处理函数参数错误

emailChangeHandler定义为接收email参数,但onChangeEmail传递的是事件对象e,导致actions.payload是事件对象而非输入值,邮箱状态无法正确更新。

修复:
修改reducer-context.js中的emailChangeHandler,改为接收事件对象并取输入值:

const emailChangeHandler = (e) => {
  dispatch({ type: "EMAIL_INPUT", payload: e.target.value });
};

同时简化Provider中的onChangeEmail绑定,直接传递函数即可:

// ValContext.Provider的value中修改
onChangeEmail: emailChangeHandler,

内容的提问来源于stack exchange,提问作者A. Lawal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 05:09:52