Flux 模型要求在 View 中启动的任何状态更改都会通过调度程序触发操作,并沿着存储传播回监听 View 。
这一切都很美好。但是,如果我需要在特定的状态更改之后执行某些 DOM 操作怎么办?在现实世界的应用程序中,我们有时必须使用称为 jQuery 插件 的遗迹(还记得那些吗?)
例如,我需要初始化一个 jQuery 视频播放器,$('#videoContainer').initPlayer(streamURL)
,在 Ajax 请求检索之后 streamURL
。 在继续阅读之前,请花点时间弄清楚如何使用 Flux/React 做到这一点。
在 View 部分,我可能会使用componentWillReceiveProps
:
var jsVideoPlayer = React.createClass({
componentWillReceiveProps: function(nextProps) {
$(this.refs.videoContainer.getDOMNode()).initPlayer(nextProps.streamURL);
},
render: function() {
return (
<div ref="videoContainer">
</div>
);
}
});
但是在这里,每次状态更新时播放器都会被初始化。那肯定会破坏我们的视频播放器。因此,在实际执行任何操作之前,我们先检查一下 URL 是否已更改:
componentWillReceiveProps: function(nextProps) {
if (nextProps.streamURL && nextProps.streamURL !== this.streamURL) {
this.streamURL = nextProps.streamURL;
$(this.refs.videoContainer.getDOMNode()).initPlayer(nextProps.streamURL);
}
},
很简单,对吧?但更大的图景又如何呢?如果我们随着添加更多功能和 UI 元素而扩大范围,这可能会导致在 View 中出现更多关于商店中发布的内容的自定义验证代码。即使在一个完美的应用程序中,每个商店都有自己的域,并且所有内容都以设计模式一尘不染,达到值得专门宗教追随者的水平,随着应用程序的扩展,我们仍然必须在 View 中使用越来越多的逻辑.
在我们的团队中,我认为这个逻辑应该真正进入 ControllerView,它适合 Flux 模型 - 但其他人都认为这是框架应该处理的事情。我们是 Flux/React 的新手,无法想出更好的主意来让框架为我们做到这一点。
最佳答案
当您识别模式时,您可以创建抽象。这里的模式是需要以简单的方式处理变化的 Prop 。我已经思考这个问题有一段时间了...这就是我到目前为止所想到的。
propUpdates: {
propName: {
onMount: function(to, el, config){}
onChange: function(from, to, el, config){},
idempotent: false,
compare: 'default'
}
}
它可能仍然很冗长,但它更具声明性,并且不需要大量 if 语句和比较。 jsbin
var Test = React.createClass({
mixins: [propUpdatesMixin],
propUpdates: {
text: {
onChange: function(from, to, el){
el.textContent = to;
},
idempotent: true
}
},
render: function(){
return <div />;
}
});
onChange 仅在属性实际更改时调用。默认情况下,它与 ===
进行比较,但是您可以指定“shallow”、“deep”的 compare
或一个采用旧的和新的 prop 值的函数,如果相等则返回 true。
如果幂等
为true,则调用onChange来代替onMount,并且第一个参数未定义
。 onChange 和 onMount 内部的 this
是组件实例。要获取具有 onChange/onMount/etc 属性的对象,请使用最后一个参数 (config)。
有时您需要更多的控制,例如当您需要对多个 Prop 的更改使用react时。
componentDidUpdate: function(prevProps){
var changes = this.getPropsChanges(prevProps, this.props);
if (changes.text.changed && changes.color.changed) {
// ...
}
}
getPropsChanges
的返回是 prop 名称到更改对象的映射。更改对象具有以下结构:
{
changed: Boolean,
from: Any,
to: Any,
name: String (prop name)
config: {onMount,onChange,idempotent,compare}
}
mixin 的设计不会妨碍您。您可以混合搭配策略。它只需要一个 propUpdates,但它可以是 {}
。您仍然可以编写自己的 componentDidMount 并仅对 onChange 使用 propUpdates,或者仅对 onMount 使用 propUpdates 并编写自己的 componentWillUpdate,或者为一个 prop 使用 onChange,为另一个 prop 使用 onMount,为第三个 prop 使用幂等 onChange,然后执行操作在 componentDidMount 和 componentDidUpdate 中。
尝试一下 jsbin看看它是否符合您的需求。为了完整起见,这里是 mixin 的完整代码。
var comparisons={"shallow":function(a,b){return Object.keys(a).every(function(key){return a[key]!==b[key]})},"deep":function(a,b){return!_.isEqual(a,b)},"default":function(a,b){return a!==b}};var EACH_PROP="__propUpdatesMixin_eachProp__";var PROPS="propUpdates";var propUpdatesMixin={};
propUpdatesMixin.componentDidMount=function(){var el=this.getDOMNode();this[EACH_PROP](function(propName,config,propValue){if(config.onMount)config.onMount.call(this,propValue,el,config);else if(config.onChange&&config.idempotent)config.onChange.call(this,undefined,propValue,el,config)},this.props)};
propUpdatesMixin.componentDidUpdate=function(prevProps){var el=this.getDOMNode();var changes=this.getPropsChanges(prevProps,this.props);Object.keys(changes).forEach(function(propName){var change=changes[propName];if(change.changed&&change.config.onChange)change.config.onChange.call(this,change.from,change.to,el,change.config)},this)};
propUpdatesMixin.getPropsChanges=function(propsA,propsB){var updates={};this[EACH_PROP](function(propName,config,a,b){var compare=typeof config.compare==="function"?config.compare:comparisons[config.compare]?comparisons[config.compare]:comparisons["default"];var changed=compare(a,b);updates[propName]={changed:changed,from:a,to:b,name:propName,config:config}},propsA,propsB);return updates};
propUpdatesMixin[EACH_PROP]=function(fn,propsA,propsB){propsA=propsA||{};propsB=propsB||{};Object.keys(this[PROPS]).map(function(propName){return fn.call(this,propName,this[PROPS][propName],propsA[propName],propsB[propName])},this)};
关于jquery - 在 Flux/React.js 中,在哪里初始化 jQuery 插件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27255517/