在 Flux/React.js 中,在哪里初始化 jQuery 插件?

Posted

技术标签:

【中文标题】在 Flux/React.js 中,在哪里初始化 jQuery 插件?【英文标题】:In Flux/React.js, where to initialize jQuery plugins? 【发布时间】:2015-01-31 01:25:45 【问题描述】:

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

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

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

在视图部分,我可能会使用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 的新手,想不出更好的主意来让框架为我们做到这一点。

【问题讨论】:

为什么不把它放在componentDidMount或componentDidUpdate中? @zackify 因为它依赖于仅在用户单击按钮时发生的 ajax 请求 为什么不直接使用诸如 VIDEO_URL_RETRIEVED 之类的操作来更新商店中的状态?如果 props streamUrl 不为空,您的控制器视图组件将接收更新的状态并在您的 jsVideoPlayer 中,那么您可以初始化您的 jQuery 插件。如果你的组件有一个纯渲染方法,它只会更新一次:当 VIDEO_URL_RETRIEVED 动作被触发时。 【参考方案1】:

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

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 的返回是 prop 名称到更改对象的映射。更改对象具有以下结构:


  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);

【讨论】:

这实际上相当不错,而且令人印象深刻。它展示了我所相信的 React 世界如何挑战开发人员更多地思考他们的架构。非常感谢您的 jsbin - 多么好的答案。我很想将其标记为正确,但我想获得更多意见:)

以上是关于在 Flux/React.js 中,在哪里初始化 jQuery 插件?的主要内容,如果未能解决你的问题,请参考以下文章

初始化发生在哪里,在 init 方法中还是在实例声明中?

我应该在 iOS/OS X 应用程序中在哪里初始化我的单例?

在 Vue js 应用程序中在哪里初始化 firebase?

在整个应用程序中使用 1 个变量,我在哪里初始化它?

你在哪里初始化视图控制器中的属性值?

UITableViewCell 的初始化在哪里?