8

Flux 模型要求在视图中启动的任何状态更改都会触发通过调度程序工作的操作,并将存储向下传播回监听视图。

通量模型

这一切都很好,花花公子。但是如果我需要在状态发生特定变化后执行一些 DOM 操作怎么办?在现实世界的应用程序中,我们有时必须使用那个,你知道的,称为jQuery 插件的遗物(还记得吗?)

例如,我需要在 Ajax 请求之后初始化一个 jQuery 视频播放器 à la 以$('#videoContainer').initPlayer(streamURL)检索。在继续阅读之前,请花点时间弄清楚如何使用Flux/React 做到这一点)。 streamURL

在视图部分,我可能会使用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 元素时将其放大并扩大范围会怎样,这可能会导致视图中出现更多自定义验证代码,以了解来自商店的内容。即使在一个完美的应用程序中,每家商店都有自己的领域,并且所有东西都以设计模式一尘不染,值得虔诚的追随者,随着应用程序的扩展,我们仍然必须在视图中使用越来越多的逻辑.

在我们的团队中,我认为这个逻辑应该真正进入符合 Flux 模型的 ControllerView 中——但其他人都认为这是框架应该处理的事情。我们是 Flux/React 的新手,对于如何让框架为我们做到这一点,我们想不出更好的主意。

4

1 回答 1

5

当您识别模式时,您可以创建抽象。这里的模式是需要以简单的方式处理不断变化的道具。我一直在考虑这个问题......这是我迄今为止想出的。

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 仅在属性实际更改时调用。默认情况下,这是比较的,===但是您可以指定一个compare“浅”、“深”或一个函数,该函数采用新旧 prop 值并在它们相等时返回 true。

如果idempotent为真,则调用 onChange 代替 onMount,第一个参数为undefined. onChange 和 onMount 内部this是组件实例。要获取具有 onChange/onMount/etc 属性的对象,请使用最后一个参数 (config)。


有时您需要更多控制,例如当您需要对多个道具的变化做出反应时。

componentDidUpdate: function(prevProps){
  var changes = this.getPropsChanges(prevProps, this.props);
  if (changes.text.changed && changes.color.changed) {
    // ...
  }
}

的返回getPropsChanges是将道具名称映射到更改对象。更改对象具有以下结构:

{
  changed: Boolean,
  from: Any,
  to: Any,
  name: String (prop name)
  config: {onMount,onChange,idempotent,compare}
}

mixin 旨在不妨碍您。您可以混合和匹配策略。它只需要一个 propUpdates,但它可以是{}. 您仍然可以编写自己的 componentDidMount 并仅将 propUpdates 用于 onChange,或者仅将 propUpdates 用于 onMount 并编写自己的 componentWillUpdate,或者将 onChange 用于一个 prop,将 onMount 用于另一个,将幂等 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)};
于 2014-12-02T19:43:54.500 回答