React中fetch获取数据后传递对象至GuestForm组件为空问题
问题解决步骤
1. 修复Edit组件的状态管理与异步请求
原代码中obj是普通变量,修改它不会触发React组件重新渲染,且fetch异步请求的执行时机错误,导致组件渲染时还没拿到接口返回的数据。修改如下:
import React, { useState, useEffect } from 'react'; import GuestForm from '../../components/Guests/GuestForm.js'; import { useParams } from 'react-router-dom'; function Edit() { const { id } = useParams(); const [mode, setMode] = useState('edit'); const [successMessage, setsuccessMessage] = useState('The guest has been edited successfully!'); const [action, setAction] = useState('/guests/edit'); // 将obj改为React状态变量,确保修改后触发组件重渲染 const [obj, setObj] = useState({first_name: '', last_name: '', email: '', password: ''}); const headers = { 'Content-Type': 'application/json;charset=UTF-8', "Accept": "application/json" }; // 用useEffect包裹异步请求,确保组件挂载/依赖变化时仅执行一次 useEffect(() => { fetch(process.env.REACT_APP_API_URL + action, { method: 'POST', headers: headers, body: JSON.stringify({data: {id: id}}), }) .then((response) => response.json()) .then((responseJson) => { // 更新状态,将接口返回的用户数据填充到obj中 setObj({ first_name: responseJson.json.guest.first_name, last_name: responseJson.json.guest.last_name, email: responseJson.json.guest.email, password: '' // 密码留空,避免返回敏感数据 }); }) .catch((error) => { console.error(error); }); }, [id, action]); // 依赖id和action,确保参数变化时重新请求 return ( <> <div className="container"> {/* 传递setObj给子组件,用于同步表单输入变化 */} <GuestForm mode={mode} successMessage={successMessage} obj={obj} action={action} setObj={setObj} /> </div> </> ); } export default Edit;
2. 修复GuestForm组件的状态同步问题
原GuestForm用props的初始值初始化内部state,之后props更新时不会同步,且提交时用的是原始props的obj,不是用户修改后的表单值。修改为受控组件,直接依赖父组件的状态:
import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import { Button, Form } from "react-bootstrap"; import axios from "axios"; import { toast } from 'react-toastify'; function GuestForm({mode, successMessage, obj, action, setObj}) { const history = useHistory(); const [details, setDetails] = useState([]); const [loading, setLoading] = useState(false); // 统一处理输入变化,同步更新父组件的obj状态 const handleInputChange = (field, value) => { setObj(prevState => ({...prevState, [field]: value})); }; const handleSave = e => { e.preventDefault(); setLoading(true); axios({ method: "POST", url: process.env.REACT_APP_API_URL + action, headers: { 'Content-Type': 'application/json;charset=UTF-8', "Accept": "application/json" }, data: { data: obj // 使用最新的obj数据提交 } }).then(result => { if(!result.data.json.error) { toast(successMessage, { position: "top-right", autoClose: 5000, hideProgressBar: false, closeOnClick: true, pauseOnHover: true, draggable: true, progress: undefined, }); history.push('/dashboard/guests'); } setDetails(result.data.json); setLoading(false); }).catch(err => { console.error(err); setLoading(false); }); }; return ( <> <div className="container"> <div className="row"> <div className="col-lg-12"> <h1 className="mt-0 mb-4 green-color">{mode === 'edit' ? 'Edit' : 'New'} Guest</h1> </div> </div> <Form onSubmit={handleSave} autoComplete="off"> <div className="row"> <div className="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2"> <Form.Group> <label htmlFor="exampleInputEmail1">Email Address</label> <Form.Control value={obj.email} onChange={e => handleInputChange('email', e.target.value)} type="email" autoComplete="off" /> </Form.Group> </div> <div className="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2"> <Form.Group> <label>Password</label> <Form.Control value={obj.password} onChange={e => handleInputChange('password', e.target.value)} type="password" autoComplete="new-password" /> </Form.Group> </div> </div> <div className="row"> <div className="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2"> <Form.Group> <label>First Name</label> <Form.Control value={obj.first_name} onChange={e => handleInputChange('first_name', e.target.value)} type="text" autoComplete="off" /> </Form.Group> </div> <div className="col-xs-12 col-sm-6 col-md-6 col-lg-6 mt-2"> <Form.Group> <label>Last Name</label> <Form.Control value={obj.last_name} onChange={e => handleInputChange('last_name', e.target.value)} type="text" autoComplete="off" /> </Form.Group> </div> </div> {(details.guest && details.error) && <div className="error-message mt-4 mb-1">{details.message}</div>} <Button className="btn-fill pull-right mt-3" type="submit" variant="info" disabled={loading} > {loading ? `${mode === 'edit' ? 'SAVE CHANGES' : 'ADD'}...` : (mode === 'edit' ? 'SAVE CHANGES' : 'ADD')} </Button> <div className="clearfix"></div> </Form> </div> </> ); } export default GuestForm;
关键修改说明
- Edit组件:将
obj转为React状态变量,用useEffect控制异步请求的执行时机,请求完成后通过setObj更新状态,触发组件重新渲染,确保GuestForm能拿到最新的用户数据。 - GuestForm组件:改为受控组件,直接使用父组件传递的
obj作为表单值,输入变化时同步更新父组件状态,确保数据实时同步;提交时直接使用最新的obj数据,避免提交过时的初始值。
内容的提问来源于stack exchange,提问作者Brian Millot
相关产品推荐
相关产品推荐

