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

使用Redux Toolkit存储表单数据:前端无显示且验证失效

问题描述

我正在开发一个使用Redux Toolkit将表单值存储到Redux Store的表单,能够在Redux DevTools中看到存储的数据,但输入的内容在表单中不显示,且输入字段的验证功能无法正常工作。

相关代码

userSlice.js

import { createSlice } from '@reduxjs/toolkit';

const initialState = {
    name: null,
    email: null,
    phone: null,
    url: null,
    message: null
}

export const userSlice = createSlice({
  name: 'user',
  initialState,
  reducers: {
    saveUser: (state, action) => {
      state.name = action.payload?.name;
      state.email = action.payload?.email;
      state.phone = action.payload?.phone;
      state.url = action.payload?.url;
      state.message = action.payload?.message;
    }
  }
})

export const { saveUser } = userSlice.actions;

export default userSlice.reducer;

store.js

import { configureStore } from '@reduxjs/toolkit';
import userReducer from '../Slice/userSlice';

export const store = configureStore ({
  reducer: {
    user: userReducer
  }
});

Contact.js

import React from 'react';
import ContactForm from '../../Forms/ContactForm';
  
function Contact() {    
    return(
        <div className="columns">
            <ContactForm />
        </div>
    )
}
  
export default Contact;

ContactForm.js

import React, { useRef, useState } from 'react';
import { Field, reduxForm } from 'redux-form';
import { useDispatch } from 'react-redux';
import validate from '../Validations/validate';
import renderTextArea from '../Fields/TextAreaInput/renderTextArea';
import renderField from '../Fields/TextInput/renderField';
import './ContactForm.css';
import { saveUser } from '../Slice/userSlice';

function ContactForm() {
    const formContainer = useRef(null);
    const dispatch = useDispatch();
    const [user, setUser] = useState({
      name: null,
      email: null,
      phone: null,
      url: null,
      message: null
    });

    const handleChange = e => {
      const { name, value } = e.target;
      setUser({ ...user, [name]: value });
      console.log("user", user)
    }

    const handleSubmit = e => {
      console.log(user, "submit data")
      e.preventDefault();
      dispatch(saveUser((user)));
    }

    return (
        <div className="container">  
          <form id="contact" ref={formContainer} onSubmit={handleSubmit}>
            <h3>Contact Us</h3>
            <fieldset>
              <Field name="name" id="name" type="text" placeholder="Your full name" component={renderField} onChange={handleChange} />
            </fieldset>
            <fieldset>
              <Field name="email" id="email" type="email" placeholder="Your email" component={renderField} onChange={handleChange} />
            </fieldset>
            <fieldset>
              <Field name="phone" id="phone" type="text" placeholder="Your phone number" component={renderField} onChange={handleChange} />
            </fieldset>
            <fieldset>
              <Field placeholder="Your Web Site" type="url" name="url" component={renderField} onChange={handleChange} />
            </fieldset>
            <fieldset>
              <Field name="message" component={renderTextArea} id="message" placeholder="Enter your message here..." rows={4} onChange={handleChange} />
            </fieldset>
            <fieldset>
              <button name="submit" type="submit" id="contact-submit">Submit</button>
            </fieldset>
          </form>
        </div>
    )
}

export default reduxForm({
    form: 'contact',
    validate,
})(ContactForm);

validate.js

const validate = val => {
    const errors = {}
    if (!val.name) {
      errors.name = 'Required';
    } else if(!/^[a-zA-Z ]+$/.test(val.name)) {
      errors.name = 'Invalid Name';
    }
    if (!val.email) {
      errors.email = 'Required';
    } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val.email)) {
      errors.email = 'Invalid email address';
    }
    if (!val.phone) {
      errors.phone = 'Required';
    } else if (val.phone.length !== 10) {
      if(/[^0-9]/.test(val.phone)) {
        errors.phone = 'Phone number must be 10 characters'
      } else {
        errors.phone = 'Invalid phone number'
      }
    } 
    if(!val.url) {
      val.url = !val.url ? "" : val.url;
    }
    if (!val.message) {
      val.message = !val.message ? "" : val.message;
    }
    return errors;
}

export default validate;
解决方案

核心问题

你同时混用了redux-form和本地useState管理表单状态,两者的状态同步逻辑冲突,导致输入不显示、验证失效。redux-form的<Field>组件本身会接管字段的状态管理,自定义的onChange和本地useState会干扰它的正常工作。

具体修复步骤

1. 移除本地状态和自定义 onChange

删除ContactForm.js中的useState声明、handleChange函数,同时移除所有<Field>组件上的onChange属性:

// 修改后的ContactForm.js核心部分
function ContactForm() {
    const formContainer = useRef(null);
    const dispatch = useDispatch();

    // 调整提交逻辑,从redux-form获取表单值
    const submitForm = (values) => {
      console.log(values, "submit data");
      dispatch(saveUser(values));
    };

    return (
        <div className="container">  
          {/* 用redux-form的handleSubmit包裹自定义提交函数 */}
          <form id="contact" ref={formContainer} onSubmit={handleSubmit(submitForm)}>
            <h3>Contact Us</h3>
            <fieldset>
              <Field name="name" id="name" type="text" placeholder="Your full name" component={renderField} />
            </fieldset>
            <fieldset>
              <Field name="email" id="email" type="email" placeholder="Your email" component={renderField} />
            </fieldset>
            <fieldset>
              <Field name="phone" id="phone" type="text" placeholder="Your phone number" component={renderField} />
            </fieldset>
            <fieldset>
              <Field placeholder="Your Web Site" type="url" name="url" component={renderField} />
            </fieldset>
            <fieldset>
              <Field name="message" component={renderTextArea} id="message" placeholder="Enter your message here..." rows={4} />
            </fieldset>
            <fieldset>
              <button name="submit" type="submit" id="contact-submit">Submit</button>
            </fieldset>
          </form>
        </div>
    )
}

2. 修正validate.js逻辑

不要修改传入的val对象,仅处理错误提示:

const validate = val => {
    const errors = {};
    if (!val.name) {
      errors.name = '必填项';
    } else if(!/^[a-zA-Z ]+$/.test(val.name)) {
      errors.name = '无效姓名';
    }
    if (!val.email) {
      errors.email = '必填项';
    } else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(val.email)) {
      errors.email = '无效邮箱地址';
    }
    if (!val.phone) {
      errors.phone = '必填项';
    } else if (val.phone.length !== 10) {
      if(/[^0-9]/.test(val.phone)) {
        errors.phone = '手机号必须是10位数字';
      } else {
        errors.phone = '无效手机号';
      }
    }
    // url和message为非必填项,无需处理,直接返回错误对象即可
    return errors;
};

export default validate;

3. 确保自定义字段组件正确接收redux-form props

redux-form的<Field>会把input(包含value、onChange等)和meta(包含验证状态)传给自定义组件,你的renderField和renderTextArea需要正确使用这些props:

// 示例renderField实现
const renderField = ({ input, placeholder, type, meta: { touched, error } }) => (
  <div className="field-container">
    <input {...input} type={type} placeholder={placeholder} />
    {/* 显示验证错误 */}
    {touched && error && <span className="error-text">{error}</span>}
  </div>
);

// 示例renderTextArea实现
const renderTextArea = ({ input, placeholder, rows, meta: { touched, error } }) => (
  <div className="field-container">
    <textarea {...input} placeholder={placeholder} rows={rows} />
    {touched && error && <span className="error-text">{error}</span>}
  </div>
);

验证效果

修复后:

  • 输入内容会实时显示在表单字段中
  • 字段失去焦点时会触发验证,错误提示正常显示
  • 提交表单时,redux-form会先验证,通过后才会调用submitForm,将正确的表单值存入Redux Store

内容的提问来源于stack exchange,提问作者Inspond

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 20:55:27