tipos superior react patrones orden entre ejemplos diseño comunicar comunicacion componentes componente javascript refactoring mixins reactjs

javascript - superior - react js ejemplos



Uso de mixins versus componentes para la reutilización de código en Facebook React (2)

Estoy comenzando a usar Facebook React en un proyecto de Backbone y hasta ahora todo va muy bien.
Sin embargo, noté cierta duplicación en mi código React.

Por ejemplo, tengo varios widgets similares a formularios con estados como INITIAL , SENDING y SENT . Cuando se presiona un botón, el formulario debe validarse, se realiza una solicitud y luego se actualiza el estado. El estado se mantiene dentro de React this.state por supuesto, junto con los valores de campo.

Si se tratara de vistas de Backbone, habría extraído una clase base llamada FormView pero mi impresión fue que React no respalda ni admite la FormView subclases para compartir la lógica de visualización (corríjame si estoy equivocado).

He visto dos enfoques para la reutilización de código en React:

¿Tengo razón en que Mixins y contenedores son preferibles a la herencia en React? ¿Es esta una decisión de diseño deliberada? ¿Tendría más sentido usar un mixin o un componente de contenedor para mi ejemplo de "widget de formulario" del segundo párrafo?

Aquí hay una esencia con FeedbackWidget y JoinWidget en su estado actual . Tienen una estructura similar, un método similar de beginSend y ambos necesitarán algún tipo de soporte de validación (todavía no existe).


Actualización: esta respuesta está desactualizada. Aléjate de los mixins si puedes. ¡Te lo adverti!
Mixins están muertos. Larga vida composición

Al principio, traté de usar subcomponentes para esto y extraer FormWidget y InputWidget . Sin embargo, abandoné este enfoque a la mitad porque quería un mejor control sobre las input generadas y su estado.

Dos artículos que me ayudaron más:

Resultó que solo necesitaba escribir dos mixins (diferentes): ValidationMixin y FormMixin .
Así es como los separé.

ValidaciónMixin

La combinación de validación agrega métodos de conveniencia para ejecutar sus funciones de validador en algunas de las propiedades de su estado y almacenar propiedades "erróneas" en una matriz state.errors para que pueda resaltar los campos correspondientes.

Fuente ( gist )

define(function () { ''use strict''; var _ = require(''underscore''); var ValidationMixin = { getInitialState: function () { return { errors: [] }; }, componentWillMount: function () { this.assertValidatorsDefined(); }, assertValidatorsDefined: function () { if (!this.validators) { throw new Error(''ValidatorMixin requires this.validators to be defined on the component.''); } _.each(_.keys(this.validators), function (key) { var validator = this.validators[key]; if (!_.has(this.state, key)) { throw new Error(''Key "'' + key + ''" is defined in this.validators but not present in initial state.''); } if (!_.isFunction(validator)) { throw new Error(''Validator for key "'' + key + ''" is not a function.''); } }, this); }, hasError: function (key) { return _.contains(this.state.errors, key); }, resetError: function (key) { this.setState({ ''errors'': _.without(this.state.errors, key) }); }, validate: function () { var errors = _.filter(_.keys(this.validators), function (key) { var validator = this.validators[key], value = this.state[key]; return !validator(value); }, this); this.setState({ ''errors'': errors }); return _.isEmpty(errors); } }; return ValidationMixin; });

Uso

ValidationMixin tiene tres métodos: validate , hasError y resetError .
Espera que la clase defina objetos validators , similar a propTypes :

var JoinWidget = React.createClass({ mixins: [React.addons.LinkedStateMixin, ValidationMixin, FormMixin], validators: { email: Misc.isValidEmail, name: function (name) { return name.length > 0; } }, // ... });

Cuando el usuario presiona el botón de envío, invoco validate . Una llamada para validate ejecutará cada validador y poblará this.state.errors con una matriz que contiene claves de las propiedades que fallaron la validación.

En mi método de render , utilizo hasError para generar una clase de CSS correcta para los campos. Cuando el usuario pone el foco dentro del campo, llamo a resetError para eliminar el resaltado del error hasta la próxima validate llamada.

renderInput: function (key, options) { var classSet = { ''Form-control'': true, ''Form-control--error'': this.hasError(key) }; return ( <input key={key} type={options.type} placeholder={options.placeholder} className={React.addons.classSet(classSet)} valueLink={this.linkState(key)} onFocus={_.partial(this.resetError, key)} /> ); }

FormMixin

La forma mixin maneja el estado del formulario (editable, enviado, enviado). Puede usarlo para desactivar entradas y botones mientras se envía la solicitud, y para actualizar su vista de manera correspondiente cuando se envía.

Fuente ( gist )

define(function () { ''use strict''; var _ = require(''underscore''); var EDITABLE_STATE = ''editable'', SUBMITTING_STATE = ''submitting'', SUBMITTED_STATE = ''submitted''; var FormMixin = { getInitialState: function () { return { formState: EDITABLE_STATE }; }, componentDidMount: function () { if (!_.isFunction(this.sendRequest)) { throw new Error(''To use FormMixin, you must implement sendRequest.''); } }, getFormState: function () { return this.state.formState; }, setFormState: function (formState) { this.setState({ formState: formState }); }, getFormError: function () { return this.state.formError; }, setFormError: function (formError) { this.setState({ formError: formError }); }, isFormEditable: function () { return this.getFormState() === EDITABLE_STATE; }, isFormSubmitting: function () { return this.getFormState() === SUBMITTING_STATE; }, isFormSubmitted: function () { return this.getFormState() === SUBMITTED_STATE; }, submitForm: function () { if (!this.isFormEditable()) { throw new Error(''Form can only be submitted when in editable state.''); } this.setFormState(SUBMITTING_STATE); this.setFormError(undefined); this.sendRequest() .bind(this) .then(function () { this.setFormState(SUBMITTED_STATE); }) .catch(function (err) { this.setFormState(EDITABLE_STATE); this.setFormError(err); }) .done(); } }; return FormMixin; });

Uso

Espera que el componente proporcione un método: sendRequest , que debería devolver una promesa Bluebird. (Es trivial modificarlo para que funcione con Q u otra biblioteca de promesas).

Proporciona métodos de conveniencia como isFormEditable , isFormSubmitting y isFormSubmitted . También proporciona un método para iniciar la solicitud: submitForm . Puede llamarlo desde el controlador onClick los botones de formulario.


Estoy construyendo un SPA con React (en producción desde hace 1 año) y casi nunca uso mixins.

El único caso de uso que tengo actualmente para mixins es cuando desea compartir el comportamiento que usa los métodos de ciclo de vida de React ( componentDidMount etc.). Este problema lo resuelven los componentes de orden superior que Dan Abramov habla en su enlace (o mediante el uso de herencia de clase ES6).

Los mixins también se usan a menudo en frameworks, para hacer que Framework API esté disponible para todos los componentes, usando la función de contexto "oculto" de React. Esto ya no será necesario con la herencia de clase ES6.

La mayoría de las otras veces, mixins se utilizan, pero realmente no se necesitan y podrían reemplazarse fácilmente con simples ayudantes.

Por ejemplo:

var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return {message: ''Hello!''}; }, render: function() { return <input type="text" valueLink={this.linkState(''message'')} />; } });

Puede refactorizar fácilmente el código LinkedStateMixin para que la sintaxis sea:

var WithLink = React.createClass({ getInitialState: function() { return {message: ''Hello!''}; }, render: function() { return <input type="text" valueLink={LinkState(this,''message'')} />; } });

¿Hay alguna gran diferencia?