Redux跨组件触发表单更新异常问题求助
React表单更新提交异常
我有Form和List两个组件:Form用于注册新用户(包含nom、prenom、email、password字段),List展示通过Form添加的用户列表。列表表格显示用户ID、姓名、邮箱,附带更新和删除按钮,删除功能正常,但更新功能存在异常:
- 点击更新按钮会调用Redux store的action,通过axios从数据库获取选中用户的完整信息(含ID)
- store中
userToUpdate状态更新后,Form组件重新渲染并正确显示该用户信息 - 但立即点击提交时,表单提示所有字段为空;点击两次更新按钮再提交,表单会提交第一次点击的用户数据;点击表单输入框触发onChange更新本地状态后再提交,错误消失且数据正确
请问为何store更新后表单数据显示正常,但需点击两次更新按钮或触发输入框onChange才能正常提交?
Form.js 代码
import axios from 'axios'; import React, { useState, useEffect } from 'react'; import { useForm } from 'react-hook-form'; import { useSelector, useDispatch } from 'react-redux'; function Form() { const [message, setMessage] = useState([]); const userToUpdate = useSelector(state => state.userToUpdate); const [formUser, setFormUser] = useState({...userToUpdate}); useEffect(() => { setFormUser({...userToUpdate}); }, [userToUpdate]) const onChange = e => { setFormUser({ ...formUser, [e.target.id]: e.target.value }) } const empty = e => { e.target.value = ""; } const { register, handleSubmit, watch, formState: { errors } } = useForm(); const onSubmit = (data) => { console.log(data); axios.post("http://localhost:8080/", data) .then(response => { setMessage([{ type: "success", content: response.data }]); }) .catch(error => { setMessage(error.response.data); setMessage([{ type: "danger", content: error.response.data }]); }); } return ( <form className='w-25 p-4 mx-auto my-3 border border-success rounded' onSubmit={handleSubmit(onSubmit)}> <div className="mb-3"> <label htmlFor="nom" className="form-label">Nom</label> <input type="text" className={"form-control" + (errors.nom ? " is-invalid" : "")} id="nom" {...register("nom", {required: true, maxLength: 30})} value={formUser.nom} onChange={onChange} /> {errors.nom && <span className='text-danger'><em>Nom</em> est requis (30 caractères max.)</span>} </div> <div className="mb-3"> <label htmlFor="prenom" className="form-label">Prénom</label> <input type="text" className={"form-control" + (errors.prenom ? " is-invalid" : "")} id="prenom" {...register("prenom", {required: true, maxLength: 30})} value={formUser.prenom} onChange={onChange} /> {errors.prenom && <span className='text-danger'><em>Prénom</em> est requis (30 caractères max.)</span>} </div> <div className="mb-3"> <label htmlFor="email" className="form-label">Email</label> <input type="email" className={"form-control" + (errors.email ? " is-invalid" : "")} id="email" {...register("email", {required: true, maxLength: 50})} value={formUser.email} onChange={onChange} /> {errors.email && <span className='text-danger'><em>Email</em> est requis (50 caractères max.)</span>} </div> <div className="mb-3"> <label htmlFor="password" className="form-label">Password</label> <input type="password" className={"form-control" + (errors.password ? " is-invalid" : "")} id="password" {...register("password", {required: true, maxLength: 30})} value={formUser.password} onChange={onChange} onClick={(event) => {if(event.target.value.length > 30){empty(event)}}} /> {errors.password && <span className='text-danger'><em>Password</em> est requis (30 caractères max.)</span>} </div> <button type="submit" className="btn btn-primary">Submit</button> {message.length > 0 ? <> <div className={'alert mt-3 mb-0 alert-' + (message && message[0].type)}>{message[0].content}<a type="button" href="/" className={'d-block mx-auto mt-2 btn btn-' + (message && message[0].type)}>Recharger</a></div> </> : <> </>} </form> ) } export default Form
List.js 代码
import axios from 'axios' import React, { useState, useEffect } from 'react' import { useDispatch } from 'react-redux'; import { actionCreators } from "../redux/index"; import { bindActionCreators } from 'redux'; function List() { const [usersList, setUsersList] = useState([]); const dispatch = useDispatch(); const AC = bindActionCreators(actionCreators, dispatch); useEffect(() => { getUsersList(); },[]); const getUsersList = async() => { const getUsers = await axios.get("http://localhost:8080/"); setUsersList(getUsers.data); } const deleteUser = async(id) => { console.log(id); axios.delete("http://localhost:8080/", {params: {id: id}}) .then(response => { getUsersList(); }) .catch(error => { }); } return ( <div className='w-50 p-4 mx-auto my-3 border border-success rounded'> <h2>Liste des utilisateurs</h2> {usersList.length <= 0 ? <h4 className='text-center'>Aucun utilisateur</h4> : <table className="table table-striped"> <thead> <tr> <th scope="col">#</th> <th scope="col">Nom</th> <th scope="col">Prenom</th> <th scope="col">Email</th> <th scope="col">Upd.</th> <th scope="col">Supp</th> </tr> </thead> <tbody> {usersList.map(element => { return ( <tr className='align-middle' key={element.id}> <th scope="row">{element.id}</th> <td>{element.nom}</td> <td>{element.prenom}</td> <td>{element.email}</td> <td><a className='btn btn-warning' onClick={() => AC.updateUser(element.id)}><i className="bi bi-pencil-fill"></i></a></td> <td><a className='btn btn-danger' onClick={() => deleteUser(element.id)}><i className="bi bi-trash3-fill"></i></a></td> </tr> ) })} </tbody> </table> } </div> ) } export default List
已尝试的解决方案
- 将store数据存入本地状态
- 使用扩展运算符复制store数据到表单本地状态
- 等待
setFormUser完成再结束useEffect
以上方法均未解决问题,如需更多信息请告知。
内容的提问来源于stack exchange,提问作者charlesfouquet
相关产品推荐
相关产品推荐

