问题:
是否有任何方法可以使用标准助焊剂工作流程 - 在组件内部使用操作和商店,并且仍然可以将此组件用于多种不同目的,或者如果没有,是否有任何方法可以在flux-react应用程序中使用复杂的嵌套结构,而不会通过巨大的回调管道传播每个更改?
示例(如果问题不够明确):
假设我有一些超级简单的自定义组件,例如 ToggleButton , Slider , DatePicker 等等。它们需要可重用,所以我不能使用它们内部的任何动作,而是我定义了回调函数。例如, DatePicker 上的onChange
会像这样触发:
this.props.onChange(data);
我有一个自定义组件,可以将其称为 InfoBox ,其中包含上述几个简单组件。该组件侦听每个子组的更改,如下所示:
<DatePicker ref='startDate' onChange={this.startDate_changeHandler} />
InfoBox 用于不同的目的,所以我猜它也不能绑定到特定的商店。
我还有一个自定义 Grid 组件,可以呈现 InfoBox 的许多实例。此网格用于在不同页面上显示不同的数据,每个页面可以有多个网格 - 所以我认为我不能将它与动作和商店绑定。
现在这里一切都变得疯狂,忍受我 - 我有几页 - 客户,产品,文章等..每个网格都至少有一个网格,每个网格都有一些过滤器(如搜索)。
页面肯定可以使用动作和存储,但页面之间有很大的相似之处,我不想复制那么多代码(不仅是方法,还有标记)。
正如您可能看到它的结构非常复杂,在我看来,对于DataPicker > InfoBox > Grid > Page > Something else
这样的嵌套组件中的每个更改实现回调方法的管道线是不对的。
答案 0 :(得分:3)
您完全正确的做法是更改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
也会将其所有道具传递给Todo
,这会将Todo
todo对象传递给InfoBox
。
所以这是处理这些事情的一种方法,但它仍然意味着你要在组件之间传播道具。在某些情况下,如果您有深度嵌套,那么传播会变得乏味,并且很容易忘记添加它会使组件进一步向下破坏。对于这些情况,我建议您查看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
1}}只关心渲染它。这是React中非常常见的模式,您可以在其中将组件委托给另一个组件,并将道具传递给它。