当前位置: 首页 > 知识库问答 >
问题:

使用React with主干时是否可以避免forceUpdate()?

夏侯渊
2023-03-14

Facebook React鼓励您将可变(state)和不可变(props)状态分开:

尽量让您的组件尽可能多的无状态。通过这样做,您可以将状态隔离到最符合逻辑的位置,并最大限度地减少冗余,从而更容易对应用程序进行推理。

当状态改变时,您应该调用setState来触发虚拟DOM diff,只有在需要时才会导致真正的DOM更新。

有一种方法可以通过调用forceUpdate手动触发DOM更新,但不鼓励:

通常情况下,您应该尽量避免使用forceUpdate(),并且只从this.propsthis.state中的渲染()读取。这使您的应用程序更加简单和高效。

但是,我看到的所有示例都忽略了这一建议,将模型和集合存储在props中,并调用forceUpdate

  • http://www.thomasboyt.com/2013/12/17/using-reactjs-as-a-backbone-view.html
  • https://github.com/usepropeller/react.backbone/blob/master/react.backbone.js
  • https://gist.github.com/ssorallen/7883081
  • http://eldar.djafarov.com/2013/11/reactjs-mixing-with-backbone/

甚至React自己的示例也使用了forceUpdate

  • https://github.com/facebook/react/blob/master/examples/todomvc-backbone/js/app.js#L148

然而,有没有更好的方法,它会带来什么好处?

共有3个答案

章安易
2023-03-14

我是骨干背后的开发者。做出反应。组件。我们使用setProps的原因是因为它仅由组件所有者(最大的父级)调用。在我看来,道具比状态更好地用于反应性更新(并传递给子组件),但是如果你能给我一些状态更好的原因,我很乐意开始朝着这个变化发展。

例如,有时我会将组件委托给其他组件,而transferPropsTo非常方便。使用状态会让实现这一目标变得更加困难。

韶英达
2023-03-14

在有更好的答案之前,让我引用React核心开发人员皮特·亨特的话:

使用骨干模型的最大胜利是它为您管理数据流。当您调用set()时,它会通知您的应用数据已更改。使用React,您会发现这不太必要,因为您所需要做的就是通过回调通知拥有状态的组件,React确保所有子级都是最新的。因此,骨干的这一部分在IMO中用处不大(无论如何,人们倾向于以这种方式在React中使用骨干)。

您不必传递纯JSON(尽管我倾向于传递纯JSON,并且它对简单数据模型很有效),但是如果您保持对象不变,您将看到很多优势。

您只需在主干模型上调用toJSON(),看看您对它的喜欢程度,而不是将模型传递给其他人。

(强调我的)

有趣的是,脊梁骨。反应组件是我发现的唯一一个使用toJSON的示例,但出于某种原因,它也使用setProps而不是setState(这也是不鼓励的)。

我根据Pete Hunt的方法(没有setProps,没有forceUpdate)做了一个简单的混合:

define(function () {

  'use strict';

  var Backbone = require('backbone'),
      _ = require('underscore');

  var BackboneStateMixin = {
    getInitialState: function () {
      return this.getBackboneState(this.props);
    },

    componentDidMount: function () {
      if (!_.isFunction(this.getBackboneState)) {
        throw new Error('You must provide getBackboneState(props).');
      }

      this._bindBackboneEvents(this.props);
    },

    componentWillReceiveProps: function (newProps) {
      this._unbindBackboneEvents();
      this._bindBackboneEvents(newProps);
    },

    componentWillUnmount: function () {
      this._unbindBackboneEvents();
    },

    _updateBackboneState: function () {
      var state = this.getBackboneState(this.props);
      this.setState(state);
    },

    _bindBackboneEvents: function (props) {
      if (!_.isFunction(this.watchBackboneProps)) {
        return;
      }

      if (this._backboneListener) {
        throw new Error('Listener already exists.');
      }

      if (!props) {
        throw new Error('Passed props are empty');
      }

      var listener = _.extend({}, Backbone.Events),
          listenTo = _.partial(listener.listenTo.bind(listener), _, _, this._updateBackboneState);

      this.watchBackboneProps(props, listenTo);
      this._backboneListener = listener;
    },

    _unbindBackboneEvents: function () {
      if (!_.isFunction(this.watchBackboneProps)) {
        return;
      }

      if (!this._backboneListener) {
        throw new Error('Listener does not exist.');
      }

      this._backboneListener.stopListening();
      delete this._backboneListener;
    }
  };

  return BackboneStateMixin;

});

它不关心你有什么样的型号或系列。

约定是主干模型进入道具,它们的JSON通过mixin自动进入状态。您需要覆盖getBackboneState(props)以使其正常工作,还可以选择watchBackboneProps告知mixin何时使用新值调用setState

用法示例:

var InfoWidget = React.createClass({
  mixins: [BackboneStateMixin, PopoverMixin],

  propTypes: {
    stampModel: React.PropTypes.instanceOf(Stamp).isRequired
  },

  // Override getBackboneState to tell the mixin
  // HOW to transform Backbone props into JSON state

  getBackboneState: function (props) {
    var stampModel = props.stampModel,
        primaryZineModel = stampModel.getPrimaryZine();

    return {
      stamp: stampModel.toJSON(),
      toggleIsLiked: stampModel.toggleIsLiked.bind(stampModel),
      primaryZine: primaryZineModel && primaryZineModel.toJSON()
    };
  },

  // Optionally override watchBackboneProps to tell the mixin
  // WHEN to transform Backbone props into JSON state

  watchBackboneProps: function (props, listenTo) {
    listenTo(props.stampModel, 'change:unauth_like_count change:is_liked');
    listenTo(props.stampModel.get('zines'), 'all');
  },

  render: function () {
    // You can use vanilla JSON values of this.state.stamp,
    // this.state.toggleIsLiked and this.state.primaryZine
    // or whatever you return from getBackboneState
    // without worrying they may point to old values
  }
}

注意:mixin需要下划线1.6.0。

颛孙俊
2023-03-14

皮特的回答很棒。

骨干模型本质上是突变的,这(虽然本身不是问题)意味着在重新渲染时,您将没有旧版本的模型进行比较。这使得通过在组件的关键位置定义应该组件更新方法来进行智能优化变得更加困难。(由于其他原因,例如实现撤消,您还失去了轻松存储旧版本模型的能力。)

调用forceUpdate只会跳过应该组件更新并强制组件重新呈现。请注意,调用渲染通常很便宜,并且只有当渲染的输出发生变化时,React才会触及DOM,所以这里的性能问题并不常见。然而,如果您可以选择使用不可变数据(包括像Pete建议的那样从到JSON()传递原始模型属性对象),我强烈推荐它。

 类似资料:
  • 我一直在思考jvm安全的工作方式。原则是,jvm始终信任并运行任何本地代码。因此,从概念上讲,如果您的代码没有显式或隐式调用<code>checkpermission(permission)</code>,这意味着它永远不会失败任何安全验证。当然,所有这些验证调用通常都是在JavaAPI类中完成的,因此我们不需要为内置权限调用它们。 现在,只要您使用内置类(如<code>FileOutputStr

  • 问题内容: 建议在HTML页面中使用表格(现在已经有了CSS)? 表格有什么用途?表具有哪些CSS所没有的功能? 问题答案: 一点都不。但是将表格用于表格数据。只是不要将它们用于一般布局。 但是,如果您显示表格数据(例如结果或什至是表格),请继续使用表格!

  • 问题内容: Facebook React 鼓励您将可变()和不可变()状态分开: 尝试使尽可能多的组件保持无状态。通过这样做,您可以将状态隔离到最逻辑的位置,并最大程度地减少冗余,从而使推理应用程序更加容易。 当状态更改时,应该调用触发虚拟DOM diff,这仅在需要时才引起真正的DOM更新。 有 是 一种方法来触发DOM更新手动调用,但它是灰心: 通常情况下 ,你应该尽量避免的所有使用和只读,并

  • 是否可以只使用附近连接的BLE,或者不切换到附近连接创建的WiFi直接网络? 对于我的问题,我使用的是P2P_STAR策略,在使用附近的连接时,不可能断开所有“客户端”与internet的连接。 谢谢你。

  • 我用的是spring 5.3.16,spring boot 2.2.10.RELEASE,spring cloud 2.2.10.RELEASE用的是spring-cloud-网飞-zuul,那么,我可以只升级spring版本到5.3.18,而不升级其他框架吗?

  • 我一直收到NoClassDefoundError错误。我试图使用JavaMail API发送电子邮件,但当我使用SMTPMessage类创建Message实例时发生错误。 我知道我需要激活库,但它还是不起作用。 错误发生在这段代码的第一行。 堆栈跟踪