【问题标题】:React JS props are not passed to child component on parent rerenderReact JS 道具不会在父重新渲染时传递给子组件
【发布时间】:2019-01-03 11:14:13
【问题描述】:

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

我有一个父组件,它将属性传递给子组件的状态。根据这个传递的属性的值,孩子最初是否显示它的视图。这是我认为 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 渲染表
从桌子收到道具
{显示:假}
显示状态的渲染模式:false

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

使用 showModal = true 渲染表
显示状态的渲染模式:false

所以当父母更新时,父母的状态不会作为道具传递给孩子!但我认为这就是它的工作原理,有人可以帮我解释一下吗?

【问题讨论】:

标签: javascript reactjs


【解决方案1】:

不,你的想法不正确。组件的构造函数不应该在每次渲染/更新时被调用。一旦它们被挂载,它们就会被调用。

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

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

  2. 你还需要这个组件的另一个prop:closeModal,它的父级应该传入一个回调方法来通过设置状态来关闭模态。

    this.setState({ showModal: false });
    

【讨论】:

  • 但是 SelectFolderModal 有一个关闭按钮,它改变了它的视图并且还必须执行一些操作。我一直在为一个不相关的组件调用的模态生命周期苦苦挣扎。我真的不知道在哪里处理这些状态,我觉得我的项目进入 Redux 有点过分了。好的,所以不是每次都调用构造函数,这是有道理的。我想我得看看一些与挂载相关的方法?
  • @FuriousGamer 父级应该保持状态。当我更新我的答案时,父母应该传递两件事:1.show prop 和 2.closeModal 的回调。
【解决方案2】:

您的代码中有一个小错误。您正在将父道具(show 道具)设置为 constructor 中的子状态,这将在最初起作用。那么每次父 props 发生变化时,子组件都会通过shouldComponentUpdate 接收到变化后的 props。

因此,在您的代码中,您可以像这样访问父道具 (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

希望对你有帮助:)

【讨论】:

    【解决方案3】:

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

    请按如下方式更改您的回调函数:

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

    此外,将此函数作为道具发送给 SelectFolderModal 组件。因为你可以调用这个函数而不是调用closeModal函数。

    请避免使用多个状态来执行单个操作。根据您的代码在 SelectFolderModal 中使用状态变量是没有意义的。

    【讨论】:

    • 你不应该直接使用状态来设置下一个状态。而不是传递 setState 一个对象字面量,你应该传递一个函数,它接收前一个状态作为它的第一个参数。
    • 嘿@HiI'mFrogatto 感谢分享伙伴。如果您编辑我的答案,我真的很感激:)
    • 请阅读setState的文档。
    猜你喜欢
    • 1970-01-01
    • 2021-09-01
    • 1970-01-01
    • 2020-08-18
    • 1970-01-01
    • 2021-12-04
    • 2018-08-12
    • 1970-01-01
    • 2020-02-13
    相关资源
    最近更新 更多