3

我正在开发一个 React 应用程序,这是我的第一个应用程序,我正在努力解决如何实现用户交互式弹出框。我有一个业务需求,需要有一个带有是/否按钮的弹出框。此弹出框由位于视图中表格中的一行中的按钮(“pobtn”)触发:

+- View ---------------------+          +- View ---------------------+
| +- Table ----------------+ |   click  | +- Table ----------------+ |
| | +- Row --------------+ | |    -->   | | +- Row --------------+ | |
| | | ...                | | |   pobtn  | | | ...                | | |
| | +--------------------+ | |          | | +--------------------+ | |
| | +- Row --------------+ | |   ,--------. +- Row --------------+ | |
| | | ...        [pobtn] | | |   | Yes/No |>| ...        [pobtn] | | |
| | +--------------------+ | |   `--------' +--------------------+ | |
| | +- Row --------------+ | |          | | +- Row --------------+ | |
| | | ...                | | |          | | | ...                | | |
| | +--------------------+ | |          | | +--------------------+ | |
| | ...                    | |          | | ...                    | |
| +------------------------+ |          | +------------------------+ |
+----------------------------+          +----------------------------+

View、Table 和 Row 都是组件。表格数据在 View 中获取并通过 props 推送到 Table,然后流入 Row,我理解这是“正确的方法”,并且是有道理的。那里没有多少地方州。

问题来自弹出框。我猜,这样做的“正确方法”似乎包括每个按钮旁边的 Popover 组件,并将道具向下推以隐藏/可见。这似乎不是很好,但在我看来更接近正确的方法。

但是,react-bootstrap 弹出窗口(我使用它是因为时间)似乎并没有以这种方式工作:

var content = <MyRowPopoverContent/>;
<OverlayTrigger trigger='click' placement='left' overlay={<Popover>{content}</Popover>
    <button className="btn btn-primary small"><i className="fa fa-trash-o"></i> Delete</button>
</OverlayTrigger>

当您单击触发按钮时,它会创建一个附加到主体的 DIV,并将其自身定位在触发元素旁边,类似于它正常工作的方式(无反应)。看起来我可以将 DIV 附加到另一个容器,但这并没有很好地记录。无论如何,覆盖内容(即 MyRowPopoverContent)类似于:

this.handleNoClick: function(){
  // Re-click the button to close; gross.
  $('[data-row-id="'+this.props.row.key+'"]').find('.fa-trash-o').parents('button').first().click();
},
this.handleYesClick: function(){
  // Do stuff...update store which would re-render the view.
  // Re-click the button to close; gross.
  $('[data-row-id="'+this.props.row.key+'"]').find('.fa-trash-o').parents('button').first().click();
},
this.render: function(){
  <div>
    <div>Are you sure?</div>
    <input placeholder="Reason"/>
    <button onClick={this.handleYesClick}>Yes</button> <button onClick={this.handleNoClick}>No</button>
  </div>
}

我开始实现该handleYesClick功能,尝试进行错误处理、异步事件等,这一切看起来都很粗略,或者至少很脆弱。我想问题是:我需要一个交互式弹出框,我该怎么做?似乎弹出窗口将是按钮或行的本地状态的一部分,或者可能像大多数事情似乎做的那样爬到顶部。

由于 react-bootstrap 已经提供了一个,使用它会很好,但我不确定如何将它与其他所有东西“挂钩”。

更新:如果它是一个简单的静态弹出框,那就太棒了;通过道具显示/隐藏它并点击pobtn是非常简单的。我纠结的部分是弹出框内的交互式内容——它一些事情(比如更新商店的事件)、显示一个微调器、确定该操作是否有效、显示错误消息或消失。即使单击“否”按钮并使弹出框消失也不清楚(我目前用 jQuery 和.click()按钮再次找到它。)通过父级的道具来做这一切似乎很可怕......

4

1 回答 1

1

假设您将弹出框封装到一个PopoverMenu组件中。这是一个对 React 友好的模式,其中父母处理其孩子的事件:

var Table = React.createClass({
  handlePopoverClick(selection, meta) {
    // selection - the selection ('Yes' or 'No' in your case)
    // meta - anything you want to know about the caller
  },
  render: function() {
    var open = /* boolean logic to determine if popover is open or closed* /;
    // key idea is to pass the child a handler from the parent
    <PopoverMenu handler={this.handlePopoverClick} open={open} ... />
    ...
  }
});

var PopoverMenu = React.createClass({
  handleClick: function(selection, meta) {
    this.props.handler(selection, meta);
  },
  render: function() {
    ...
    //somewhere you render the 'Yes' Button of this popover
    <Button onClick={this.handleClick.bind(this, 'Yes', {row: 2})} />
  }
});

PopoverMenu组件是无状态的;它只知道渲染。它的处理程序接收点击事件,然后根据PopoverMenu需要调整 's props。神奇之处在于.bind,它允许您注入特定于调用者的信息以供处理程序做出反应。(您可以想象.bind发生在map调用 whereYes{row :2}是动态变量。)请注意,您可以使用父级中的单个处理程序处理所有弹出框选择,并且您可以轻松跟踪,作为父级状态的一部分(例如行) ,任何呼叫的等待/成功/失败状态。这可能看起来很可怕,但是说逻辑需要存在于某个地方:) 如果那个地方不是孩子,那就更好了,因为 React 方式是无状态的孩子。当信息从父母流向孩子时,状态自然成为道具。

也就是说,如果您的父母做太多的簿记工作并且您没有从子组件中获得干净、独立的行为,您可以将逻辑下推到子组件中,通常以子组件的可重用性为代价。孩子处理自己的打开/关闭状态和等待/错误/成功状态是合理和可能的。如果你走这条路,你可以使用的一种模式是让孩子处于等待状态,直到并且除非你通过道具从父母那里收到匹配的值。例如,如果用户进行了需要回调的菜单选择“X”,则孩子会这样做setState{ selection: 'X'}并等待父母通过道具(反映商店的状态)来证实:

if (this.state.selection !== this.props.selection) {
  //render component as waiting
} else {
  //render as normal
}

关于弹出框的打开/关闭状态,如果您改为使用像ButtonDropdown管理自己的打开/关闭状态的组件会更容易。(您可能必须 onSelect在下拉列表中设置一个空处理程序,以便在选择时自动关闭。)

于 2015-08-29T18:26:55.943 回答