when what shouldcomponentupdate react purecomponent pure component change causes javascript backbone.js reactjs

javascript - what - ¿Puedo evitar forceUpdate() cuando uso React with Backbone?



shouldcomponentupdate (3)

Hasta que haya una mejor respuesta, permítanme quote Pete Hunt , un desarrollador central de React:

La gran victoria con los modelos de Backbone fue que administró su flujo de datos para usted. Cuando llamaste a set() informaría a tu aplicación que los datos habían cambiado. Con React encontrará que esto es menos necesario porque todo lo que necesita hacer es informar al componente que posee el estado a través de una devolución de llamada y React se asegura de que todos los niños estén al día. Entonces, esta parte de la red troncal es menos útil (y la gente tiende a usar la red troncal de esta manera con React de todos modos).

No es necesario pasar JSON puro (aunque eso es lo que suelo hacer y funciona bien para modelos de datos simples), pero verá muchas ventajas si mantiene sus objetos inmutables.

Puede probar esto simplemente llamando a toJSON() en sus modelos principales y viendo cómo le gusta vs pasando los modelos.

(énfasis mío)

Curiosamente, Backbone.React.Component es el único ejemplo que encontré que usa toJSON , pero por alguna razón también usa setProps lugar de setState (que también se setState ).

Actualizar

Hice una mezcla simple basada en el enfoque de Pete Hunt (no setProps , no 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; });

No le importa qué tipo de modelos o colecciones tiene.

La convención es que los modelos Backbone van en props y su JSON es puesto automáticamente por mixin en state . getBackboneState(props) sobrescribir getBackboneState(props) para que esto funcione, y opcionalmente watchBackboneProps para decirle a la mezcla cuándo llamar a setState con valores nuevos.

Ejemplo de uso:

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 } }

Nota: mixin requiere Underscore 1.6.0+.

Facebook React lo encourages a separar state mutable ( state ) e inmutable ( props ):

Trate de mantener tantos de sus componentes como sea posible sin estado. Al hacer esto, aislará el estado en su lugar más lógico y minimizará la redundancia, haciendo que sea más fácil razonar sobre su aplicación.

Cuando el estado cambia, se supone que debe llamar a setState para desencadenar DOM virtual diff, lo que causará una actualización DOM real solo cuando sea necesario.

Hay una forma de desencadenar manualmente la actualización de DOM llamando a forceUpdate pero no se forceUpdate :

Normalmente deberías tratar de evitar todos los usos de forceUpdate() y solo leer desde this.props y this.state en render() . Esto hace que su aplicación sea mucho más simple y más eficiente.

Sin embargo, todos los ejemplos de React + Backbone que he visto ignoran este consejo y almacenan modelos y colecciones en props y llaman a forceUpdate :

Incluso el propio ejemplo de React usa forceUpdate :

¿Hay una mejor manera, sin embargo, y qué beneficios le daría?


La respuesta de Pete es genial.

Los modelos troncales son intrínsecamente mutantes, lo que (si bien no es un problema en sí mismo) significa que al reindicarse, no tendrá la versión anterior del modelo para comparar. Esto hace que sea más difícil realizar optimizaciones inteligentes definiendo los métodos de shouldComponentUpdate en lugares clave en sus componentes. (También pierde la posibilidad de almacenar fácilmente versiones antiguas de su modelo por otros motivos, como implementar deshacer ).

Llamar a forceUpdate simplemente omite shouldComponentUpdate y obliga al componente a rerender. Tenga en cuenta que la invocación de render suele ser económica, y React solo tocará el DOM si la salida del render ha cambiado, por lo que los problemas de rendimiento aquí no son comunes. Sin embargo, si tiene la opción de utilizar datos inmutables (incluido el toJSON() de objetos de propiedad del modelo sin procesar desde toJSON() como sugiere Pete), lo recomendaría encarecidamente.


Soy el desarrollador detrás de Backbone.React.Component. La razón por la que estamos usando setProps es porque el único propietario del componente (padre principal) lo llamará. La forma en que lo veo, apoyos es mejor usar para actualizaciones reactivas (y pasar a componentes secundarios) que declarar, pero si puedes señalarme algunas razones por las cuales el estado es mejor, estaré encantado de comenzar a desarrollar ese cambio.

Por ejemplo, a veces tengo componentes que delegar en otros, donde transferPropsTo es bastante útil. Usar el tipo de estado hace que sea más difícil lograr eso.