React中父组件如何向子组件传值?表单用props还是内部state更合适?
问题根因
你之前用子组件state绑定表单为空的核心原因很简单:父组件第一次渲染PlayerForm的时候,componentDidMount里的接口请求还没返回,父组件state的firstname、lastname都是初始空值,子组件的constructor仅在初始化时执行一次,所以子组件state拿到的初始值就是空的。等父组件接口返回更新state、把新的props传给子组件时,子组件不会重新执行constructor更新自己的state,自然表单就一直是空。
你现在直接用props绑定表单的写法存在隐藏bug:你输入内容的时候,onChange修改的是子组件自己的state,但input的value绑定的是父组件传下来的props,父组件的state没有同步更新,所以你输入任何内容输入框都不会有变化,只是暂时没触发这个问题而已,这不是正确的实现。
两种正确的实现方案
你要复用表单同时支持新增和更新,推荐选下面两种方案其中一种:
方案1:子组件做完全受控组件(推荐,复用更简单)
所有表单状态完全由父组件托管,子组件不维护自己的state,只负责触发状态变更回调:
父组件修改点
import React, { Component } from 'react'; import axios from 'axios'; import PlayerForm from './PlayerForm'; class UpdatePlayer extends Component { constructor(props) { super(props); this.state = { id: props.match.params.id, firstname: '', lastname: '' }; } componentDidMount = async () => { try { const id = this.state.id; const response = await axios.get(`http://localhost:8000/players/${id}`); this.setState({ firstname: response.data.firstname, lastname: response.data.lastname }); } catch (error) { alert(error) } } handleSubmit = (firstname, lastname) => { const data = { firstname, lastname } // 后续提交更新逻辑写在这里 } // 新增状态更新回调,接收子组件的输入变化 handleChange = (field, value) => { this.setState({[field]: value}) } render() { return ( <PlayerForm data={this.state} buttonLabel="Update" onSubmit={this.handleSubmit} onChange={this.handleChange} /> ) } } export default UpdatePlayer;
子组件修改点
import React, { Component } from 'react'; class PlayerForm extends Component { handleSubmit = (event) => { event.preventDefault(); const { firstname, lastname } = this.props.data; this.props.onSubmit(firstname, lastname); } onChange = (event) => { this.props.onChange(event.target.name, event.target.value); } render() { const { firstname, lastname } = this.props.data; return ( <form onSubmit={this.handleSubmit} method="post"> <div> <label>Firstname</label> <input className="input" placeholder="Firstname" name="firstname" value={firstname} onChange={this.onChange}/> </div> <div> <label>Lastname</label> <input className="input" placeholder="Lastname" name="lastname" value={lastname} onChange={this.onChange}/> </div> <button className="button is-primary" type="submit"> {this.props.buttonLabel}</button> </form> ) } } export default PlayerForm;
新增场景下父组件初始值给空串即可,不需要修改子组件代码。
方案2:子组件自己维护state(符合你想要内部用state的需求)
子组件自己管理表单状态,但是要监听props的变化同步更新自身state,之前没生效是因为你没加判断逻辑,正确写法如下:
子组件修改点
import React, { Component } from 'react'; class PlayerForm extends Component { constructor(props) { super(props); this.state = { firstname: props.data.firstname, lastname: props.data.lastname } } // 新增生命周期,仅当props传入的data和当前state不一致时才更新 static getDerivedStateFromProps(nextProps, prevState) { if (nextProps.data.firstname !== prevState.firstname || nextProps.data.lastname !== prevState.lastname) { return { firstname: nextProps.data.firstname, lastname: nextProps.data.lastname } } // 不需要更新就返回null return null } handleSubmit = (event) => { event.preventDefault(); const { firstname, lastname } = this.state; this.props.onSubmit(firstname, lastname); } onChange = (event) => { this.setState({ [event.target.name]: event.target.value }); } render() { // 绑定子组件自己的state const { firstname, lastname } = this.state; return ( <form onSubmit={this.handleSubmit} method="post"> <div> <label>Firstname</label> <input className="input" placeholder="Firstname" name="firstname" value={firstname} onChange={this.onChange}/> </div> <div> <label>Lastname</label> <input className="input" placeholder="Lastname" name="lastname" value={lastname} onChange={this.onChange}/> </div> <button className="button is-primary" type="submit"> {this.props.buttonLabel}</button> </form> ) } } export default PlayerForm;
这种方案适合表单逻辑比较复杂,不想把状态都放到父组件的场景,新增场景下父组件传空值的data即可。
内容的提问来源于stack exchange,提问作者Ruben
相关产品推荐
相关产品推荐

