如何在React子组件ShippingBlock中修改父组件的shippingMethod变量
问题根因
- 父组件更新状态的方式错误:React禁止直接修改
this.state的值,必须调用setState方法才能触发状态更新和组件重渲染,你之前直接赋值this.state.shippingMethod = method不会生效,下次组件渲染时状态还是旧值。 - 子组件状态与父组件不同步:你将父组件传入的
shippingMethod赋值给子组件自身的defaultMethod状态,但是constructor仅在组件挂载时执行一次,后续父组件传入的shippingMethod更新时,子组件的defaultMethod不会同步更新,导致下拉框的选中值永远是子组件本地的旧值。 - 子组件修改本地状态的方式错误:子组件的
setShippingMethod方法里直接修改this.state.defaultMethod,没有调用setState,不会触发子组件重渲染,下拉框自然不会更新选中值。
修复方案
采用状态提升逻辑,shippingMethod状态完全由父组件管理,子组件仅负责接收参数和触发更新即可,无需额外维护本地重复状态:
父组件修改后代码
import React from 'react'; import "./Checkout.scss" import AddressBlock from './AddressBlock'; import PaymentBlock from './PaymentBlock'; import ShippingBlock from './ShippingBlock'; import TotalsBlock from './TotalsBlock'; import { Modal, ModalHeader, ModalBody } from 'reactstrap'; class CheckoutModal extends React.Component { constructor(props) { super(props); this.state = { name: 'React', shippingMethod: null }; // 提前绑定this,避免每次render重复生成函数 this.setShippingMethod = this.setShippingMethod.bind(this); } setShippingMethod(method) { // 调用setState更新状态触发重渲染 this.setState({ shippingMethod: method }); } render() { const buttonStyles = { position: "absolute", top: "-35px", right: "10px", border: "0", background: "none" }; return ( <Modal id="checkout" isOpen size='xl'> <button onClick={this.props.onRequestClose} style={buttonStyles}>x</button> <ModalHeader className="header"> Checkout </ModalHeader> <ModalBody> <div className="row"> <AddressBlock /> <ShippingBlock setShippingMethod={this.setShippingMethod} shippingMethod={this.state.shippingMethod}/> </div> <div className="row"> <PaymentBlock /> <TotalsBlock /> </div> </ModalBody> </Modal> ); } } export default CheckoutModal;
子组件修改后代码
import React from 'react'; import Config from 'config'; import "./Checkout.scss" class ShippingBlock extends React.Component { constructor(props) { super(props); this.state = { name: 'React', shippingMethods: [], }; this.setShippingMethod = this.setShippingMethod.bind(this); } async componentDidMount() { console.log('Running componentDidMount') const tokenString = sessionStorage.getItem("token"); const token = JSON.parse(tokenString); let headers = new Headers({ "Accept": "application/json", "Content-Type": "application/json", 'Authorization': 'Bearer ' + token.token }); const response = await fetch(Config.apiUrl + `/api/Shipping/GetShippingMethods`, { method: "GET", headers: headers }); const json = await response.json(); console.log(json); this.setState({ shippingMethods: json }); // 没有默认配送方式时拉取默认值同步到父组件 if (this.props.shippingMethod === null) { const mresponse = await fetch(Config.apiUrl + `/api/Users/GetDefaultShippingMethod`, { method: "GET", headers: headers }); const mjson = await mresponse.json(); console.log(mjson); this.props.setShippingMethod(mjson); } } setShippingMethod(e) { console.log('Shipping method ' + JSON.stringify(e.target.value)); // 直接调用父组件方法更新全局状态 this.props.setShippingMethod(e.target.value); } render() { const shippingMethods = this.state.shippingMethods; // 直接使用父组件传入的配送方式作为下拉框选中值 const currentMethod = this.props.shippingMethod; return ( <aside id="checkout" className="block col-1"> <h1>Shipping</h1> <div className="row"> <select id="comboMethod" onChange={this.setShippingMethod} value={currentMethod === null ? '' : currentMethod.trim()}> {shippingMethods.map(method => { return <option key={method.shipmthd.trim()} value={method.shipmthd.trim()}>{method.shipmthd.trim()}</option>} )} </select> </div> </aside> ); } } export default ShippingBlock;
内容的提问来源于stack exchange,提问作者Randy
相关产品推荐
相关产品推荐

