React JS道具不会在父渲染时传递给子组件

时间:2019-01-03 11:14:14

标签: javascript reactjs

我知道这个问题以前曾被问过,但答案似乎总是暗示着与这种情况无关的问题,我只是看不到。

我有一个父组件,该组件将属性传递给子组件的状态。根据此传递的属性的值,子代最初是否显示其视图。这是我思考 React的方式:

当父(或一般组件)的状态更改时,它将重新呈现。重新渲染后,它的所有子代都再次收到其道具(即,是否也调用了子代的构造函数?)。但这显然不是此示例中的工作方式。

父项:

class FileListTable extends React.Component
{
    constructor(props)
    {
        super(props);

        this.state = { 
            showModal: false
        };

        this.showSelectFolderModal = this.showSelectFolderModal.bind(this);
    }

    showSelectFolderModal()
    {
        this.setState({ showModal: true });
    }

    render()
    {       
        console.log("Rendering table with showModal = " + this.state.showModal);
        return (
            <div>
                <table id="files-table" class="table">
                    <tr>
                <td>
                    ...
                    <span class="float-right">
                        <FileActionMenu 
                            showSelectFolderModal={ this.showSelectFolderModal } />
                    </span>
                </td>
            </tr>
                </table>
                <SelectFolderModal 
                    show={ this.state.showModal }/>
            </div>
        );
    }
}

FileActionMenu子组件:

class FileActionMenu extends React.Component
{
    constructor(props)
    {
        super(props);
    }

    render()
    {
        return (
            <div class="dropdown">
            <button class="btn dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                ...
            </button>
            <div class="dropdown-menu dropdown-menu-right">            
                <button class="dropdown-item" type="button" onClick={ () => this.props.showSelectFolderModal() }>...</button>                  
            </div>
        </div>
        );
    }
}

SelectFolderModal子组件:

class SelectFolderModal extends React.Component
{
    constructor(props)
    {
        super(props);

        console.log("Received props from table");
        console.log(this.props);
        this.state = { show: this.props.show };
    }

    closeModal()
    {
        this.setState({ show: false });
        // do something post close
    }

    render()
    {
        console.log("Rendering modal with show status: " + this.state.show);

        return (
            <div id="select-folder-modal" class="modal { this.state.show ? 'show' }" tabindex="-1" role="dialog">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                            <span aria-hidden="true">&times;</span>
                        </button>
                    </div>
                    <div class="modal-body">
                        ...
                    </div>
                    <div class="modal-footer">
                        <button type="button" class="btn btn-secondary" data-dismiss="modal" onClick={ this.closeModal }>...</button>
                    </div>
                </div>
            </div>
        </div>
        );
    }
}

我的初始控制台输出是这样:

  

具有showModal = false的渲染表
  桌上收到的道具
  {show:false}
  显示状态为false的渲染模态

好的,这很有道理。但是,当我单击FileActionMenu中的按钮时,会得到以下输出:

  

具有showModal = true的渲染表
  显示状态为false的渲染模态

因此,当父母正在更新时,父母的状态不会作为道具传递给孩子!但是我认为这是这样的,有人可以帮我一下吗?

3 个答案:

答案 0 :(得分:0)

不,您的想法不正确。组件的构造函数不应在每次渲染/更新时都被调用。一旦被安装,它们就会被呼叫。

回到您的示例,SelectFolderModal应该是无状态的。实际上,父级维护应用程序的“状态”。没有什么可以阻止您在render方法中使用“道具”:

  1. SelectFolderModal的render方法中,请使用props而不是state,因为该组件应该是无状态的。

  2. 该组件还需要另一个道具:closeModal,哪个父级应该传入回调方法以通过设置状态来关闭模式。

    this.setState({ showModal: false });
    

答案 1 :(得分:0)

您的代码中有一个小错误。您正在将show中的父道具(constructor道具)设置为子状态,该状态最初会起作用。然后,每次父项道具更改时,子项组件都通过shouldComponentUpdate接收更改的道具。

因此,在您的代码中,您可以像这样访问父项(show):

  1. 添加类似于showSelectFolderModal的函数,将show的值更改为false
  2. 然后将函数传递给子组件,然后将其分配给按钮而不是closeModal函数。

class FileListTable extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      showModal: false
    };

    this.showSelectFolderModal = this.showSelectFolderModal.bind(this);
    this.hideSelectFolderModal = this.hideSelectFolderModal.bind(this);
  }

  showSelectFolderModal() {
    this.setState({ showModal: true });
  }

  hideSelectFolderModal() {
    this.setState({ showModal: false });
  }

  render() {
    console.log(this.state);
    return (
      <div>
        <table id="files-table" class="table">
          <tr>
            <td>
              Main Table
              <span class="float-right">
                <FileActionMenu
                  showSelectFolderModal={this.showSelectFolderModal}
                />
              </span>
            </td>
          </tr>
        </table>
        <SelectFolderModal
          show={this.state.showModal}
          hideSelectFolderModal={this.hideSelectFolderModal}
        />
      </div>
    );
  }
}

class FileActionMenu extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div class="dropdown">
        <div class="dropdown-menu dropdown-menu-right">
          <button
            class="dropdown-item"
            type="button"
            onClick={() => this.props.showSelectFolderModal()}
          >
            event button
          </button>
        </div>
      </div>
    );
  }
}

class SelectFolderModal extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    console.log(this.props.show);
    return (
      <div
        id="select-folder-modal"
        class={`modal ${this.props.show ? "show" : ""}`}
        tabIndex="-1"
        role="dialog"
      >
        <div class="modal-dialog" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <button
                type="button"
                class="close"
                data-dismiss="modal"
                aria-label="Close"
              >
                <span aria-hidden="true">&times;</span>
              </button>
            </div>
            <div class="modal-body">...</div>
            <div class="modal-footer">
              <button
                type="button"
                class="btn btn-secondary"
                data-dismiss="modal"
                onClick={() => this.props.hideSelectFolderModal()}
              >
                hide
              </button>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

ReactDOM.render(<FileListTable />, document.getElementById("root"));

这是现场demo

希望它会有所帮助:)

答案 2 :(得分:0)

小的代码更改可能会帮助您解决问题。

请按如下所示更改您的回调函数:

showSelectFolderModal()
{
    this.setState({ showModal: !this.state.showModal });
}

此外,将此功能作为道具发送到SelectFolderModal组件。因为您可以调用此函数而不是调用closeModal函数。

请避免使用多个状态来执行单个操作。按照代码在SelectFolderModal中使用状态变量毫无意义。