react-flux 应用程序的可重用性/可扩展性问题
Posted
技术标签:
【中文标题】react-flux 应用程序的可重用性/可扩展性问题【英文标题】:Reusability/Scalability issues with react-flux app 【发布时间】:2015-07-09 10:58:38 【问题描述】:问题:
有什么方法可以拥有标准的通量工作流程 - 在组件内部使用 Actions 和 Stores 并且仍然能够将该组件用于多种不同的目的,或者如果没有,有没有办法在通量反应应用程序中拥有复杂的嵌套结构,而无需通过巨大的回调管道传播每个更改?
示例(如果问题不够清楚):
假设我有几个超级简单的自定义组件,例如 ToggleButton、Slider、DatePicker 等等。它们需要可重用,因此我不能在其中使用任何操作,而是定义了回调函数。例如,DatePicker 上的 onChange
会像这样触发:
this.props.onChange(data);
我有一个自定义组件,我们称它为 InfoBox,其中包含上面描述的几个简单组件。这个组件像这样监听它的每个子组件的变化:
<DatePicker ref='startDate' onChange=this.startDate_changeHandler />
InfoBox 用于不同的目的,所以我猜它也不能绑定到特定的商店。
我还有一个自定义的Grid 组件,它可以呈现InfoBox 的许多实例。这个网格用于在不同的页面上显示不同的数据,每个页面可以有多个网格 - 所以我认为我不能将它与 Actions 和 Stores 绑定。
现在这一切都变得疯狂了,请耐心等待 - 我有几页 - 客户、产品、文章等.. 他们每个人都至少有一个 Grid 并且每个网格都有一些过滤器(如 search)。
页面肯定可以使用动作和存储,但页面之间有很大的相似之处,我不想复制那么多代码(不仅是方法,还有标记)。
您可能会看到它的结构相当复杂,在我看来,为嵌套组件中的每个更改实现回调方法的管道是不正确的,例如DataPicker > InfoBox > Grid > Page > Something else
。
【问题讨论】:
【参考方案1】:您完全正确,更改 DatePicker
组件中的日期不应触发 Flux 操作。 Flux 动作用于更改应用程序状态,并且几乎从不查看状态,其中视图状态表示“输入框 X 包含值 Z”或“列表 Y 已折叠”。
很高兴您正在创建像Grid
等可重用组件,它将帮助您使应用程序更易于维护。
处理您的问题的方法是将组件从顶层传递到底层。这可以通过子组件或简单的道具来完成。
假设您有一个页面,其中显示了两个网格,一个网格 - 比方说 - 会议约会和一个包含待办事项的网格。现在页面本身在层次结构中太高了,不知道何时触发动作,而您的Grid
和InfoBox
太笼统,不知道要触发哪些动作。你可以像你说的那样使用回调,但这可能有点太有限了。
所以你有一个页面,你有一个约会数组和一个待办事项数组。要渲染它并将其连接起来,你可能会有这样的事情:
var TodoActions =
markAsComplete: function (todo)
alert('Completed: ' + todo.text);
;
var InfoBox = React.createClass(
render: function()
return (
<div className="infobox">
React.createElement(this.props.component, this.props)
</div>
);
);
var Grid = React.createClass(
render: function()
var that = this;
return (
<div className="grid">
this.props.items.map(function (item)
return <InfoBox component=that.props.component item=item />;
)
</div>
);
);
var Todo = React.createClass(
render: function()
var that = this;
return (
<div>
Todo: this.props.item.text
<button onClick=function () TodoActions.markAsComplete(that.props.item); >Mark as complete</button>
</div>
);
);
var MyPage = React.createClass(
getInitialState: function ()
return
todos: [text: 'A todo']
;
,
render: function()
return (
<Grid items=this.state.todos component=Todo />
);
);
React.render(<MyPage />, document.getElementById('app'));
如您所见,Grid
和InfoBox
都知道的很少,除了一些数据传递给它们,并且它们应该在底部渲染一个知道如何触发动作的组件。 InfoBox
也将它的所有 props 传递给 Todo
,这将给 Todo
传递给 InfoBox
的 todo 对象。
所以这是处理这些事情的一种方法,但这仍然意味着您正在从一个组件向下传播道具。在某些情况下,您有深度嵌套,传播变得乏味,并且很容易忘记添加它,这会进一步分解组件。对于这些情况,我建议您查看 React 中的上下文,这非常棒。这是对上下文的一个很好的介绍:https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html
编辑
更新您的评论的答案。为了概括示例中的Todo
,使其不知道要显式调用哪个操作,您可以将其包装在一个知道的新组件中。
类似这样的:
var Todo = React.createClass(
render: function()
var that = this;
return (
<div>
Todo: this.props.item.text
<button onClick=function () this.props.onCompleted(that.props.item); >Mark as complete</button>
</div>
);
);
var AppointmentTodo = React.createClass(
render: function()
return <Todo ...this.props onCompleted=function (todo) TodoActions.markAsComplete(todo); />;
);
var MyPage = React.createClass(
getInitialState: function ()
return
todos: [text: 'A todo']
;
,
render: function()
return (
<Grid items=this.state.todos component=AppointmentTodo />
);
);
因此,现在不是让MyPage
将Todo
传递给Grid
,而是将AppointmentTodo
传递给仅作为知道特定操作的包装组件,从而释放Todo
以只关心渲染它。这是 React 中非常常见的模式,其中您的组件只是将渲染委托给另一个组件,并将 props 传递给它。
【讨论】:
感谢您的回答,但在接受之前我有一个小要求 - 请在您的示例中包括如何使用Todo
组件来处理检查约会 i> 以及 Todos,我不确定您是否需要将操作作为属性传递,或者有没有更好的方法?
我不确定我是否理解。在我的示例中,待办事项和约会是两个独立的事物,彼此没有任何关系。这也是我在示例中省略约会的原因,因为您只需复制我在 todos 中给出的方法。你能详细说明你想让我展示什么吗?在待办事项的上下文中,“检查约会”是什么意思?
可以说appointments 也是与todo 列表完全相同的清单,它们只是使用来自不同商店的数据。在这种情况下,我需要能够重用 Todo
类。
我已经更新了我的答案。希望它能回答你的问题。
我希望有更灵活的解决方案,但我意识到可能没有,谢谢您的时间。以上是关于react-flux 应用程序的可重用性/可扩展性问题的主要内容,如果未能解决你的问题,请参考以下文章