我有一个反应模式,它呈现它的孩子:
// base_modal.js
import React, { Component } from 'react';
import Modal from 'react-modal';
export default class BaseModal extends Component {
componentDidMount() {
console.log("BaseModal mounted");
}
render() {
return (
<Modal
isOpen={this.props.showModal}
className="base-modal"
overlayClassName="base-modal-overlay"
onRequestClose={this.props.onRequestClose}
contentLabel="base-modal"
>
{this.props.children}
</Modal>
);
}
}
我有另一个组件在一些孩子中渲染BaseModal:
// my_modal.js
import BaseModal from './base_modal'
export default class MyModal extends Component {
constructor(props) {
super(props);
this.doSomeSetup = this.doSomeSetup.bind(this);
}
componentDidMount() {
console.log("MyModal mounted");
this.doSomeSetup();
}
doSomeSetup() {
console.log(this.dateInput); // undefined
}
render() {
return (
<BaseModal
showModal={this.props.showModal}
onRequestClose={this.props.onClose}
>
<form>
<div>
<input
ref={ input => { console.log("In the input ref"); this.dateInput = input; }}
type='text'
value={this.state.inputVal}
onChange={this.handleChange}
/>
</div>
</form>
</BaseModal>
)
}
}
我有一个呈现MyModal的组件:
// my_view.js
import MyModal from './my_modal'
export default class MyView extends Component {
constructor(props) {
super(props)
this.state = {
showModal: false
}
this.showModal = this.showModal.bind(this);
this.hideModal = this.hideModal.bind(this);
}
showModal() {
this.setState({
showModal: true
});
}
hideModal() {
this.setState({
showModal: false
});
}
render() {
return (
<div>
<Button onClick={this.showModal} />
<MyModal
showModal={false}
onClose={this.hideModal}
/>
</div>
)
}
}
问题是在MyModal中,组件安装时未定义this.dateInput。每当我渲染MyView时,它渲染MyModal,但MyModal渲染BaseModal,只有当它的“isOpen”设置为true时才会渲染它的子节点。
当我渲染MyView时,我看到控制台记录了“BaseModal已安装”,“MyModal已安装”,然后是doSomeSetup方法中的console.log的“undefined”(即,尚未执行ref回调)。当我单击MyView中的按钮打开模态时,我看到console.log“在输入引用中”。因此,我想要在MyModal中输入的设置没有发生,因为实际的表单输入的ref仅在React Modal将prop isOpen设置为true时执行。
另一种设置是进行以下更改:
// my_modal.js
componentDidMount() {
console.log("MyModal mounted");
}
doSomeSetup(input) {
console.log(input);
}
(...) ref={ input => { console.log("In the input ref"); this.doSomeSetup(input); }}
但是,现在,存在以下行为。当我渲染MyView时,我看到“BaseModal已安装”,“MyModal已安装”。当我单击Button打开模态时,我看到“在输入引用中”,实际的输入元素获得console.logged。
这是奇怪的事情(尽管我写这篇文章时我开始看到自己怀疑的答案):假设我在MyModal的渲染方法中添加了一个按钮,它只是改变了inputVal的状态,迫使重新投降:
changeState() {
this.setState({
inputVal: this.state.inputVal + 1
});
}
render() {
return (
<BaseModal
showModal={this.props.showModal}
onRequestClose={this.props.onClose}
>
<button onClick={this.changeState}
<form>
<div>
<input
ref={ input => { console.log("In the input ref"); this.doSomeSetup(input); }}
type='text'
value={this.state.inputVal}
onChange={this.handleChange}
/>
</div>
</form>
</BaseModal>
)
}
现在当我点击按钮是“在输入引用中”时会发生什么,然后执行doSomeSetup的console.log并生成“null”,然后我得到另一个“在输入引用中”,然后是控制台来自doSomeSetup的.log生成一个实际的输入元素。所以实际上看起来传递给BaseModal(我的按钮和窗体)的子项首先被卸载,然后再次挂载(我认为这是因为当节点将卸载时,以null作为参数调用ref回调)。这很奇怪。
所以现在,我想我的问题是:上述内容是否有意义,并且父组件每次卸载并安装其子项是否正常?这甚至是描述正在发生的事情的正确方法吗?
更准确地说,我想要的功能是在doSomeSetup中为输入添加一个事件处理程序。但似乎任何时候输入都要改变,因为它包含在BaseModal的子节点中,它是BaseModal的支柱,后者以某种方式决定卸载并挂载子节点。为什么不只是让孩子们重新投降呢?有更好的方法吗?