Facebook React'te kodun yeniden kullanılması için mixin ve bileşenlerin kullanılması


116

Facebook React'i bir Backbone projesinde kullanmaya başlıyorum ve şu ana kadar gerçekten iyi gidiyor.
Ancak, React koduma sızan bazı yinelemeler fark ettim.

Örneğin , ve gibi durumlara sahip birkaç form benzeri widget'ım var . Bir düğmeye basıldığında, formun doğrulanması, bir talepte bulunulması ve ardından durum güncellenmesi gerekir. Durum, alan değerleriyle birlikte tabii ki React içinde tutulur .INITIALSENDINGSENTthis.state

Bunlar Backbone görünümleri olsaydı, adı verilen bir temel sınıf çıkarırdım,FormView ancak benim izlenimim React'in görünüm mantığını paylaşmak için alt sınıflamayı ne desteklediği ne de desteklediğiydi (yanılıyorsam düzeltin).

React'te kodun yeniden kullanımına yönelik iki yaklaşım gördüm:

React'te mixin ve kapların kalıtıma tercih edildiği konusunda haklı mıyım? Bu bilinçli bir tasarım kararı mı? İkinci paragraftaki "form widget" örneğim için bir mixin veya bir konteyner bileşeni kullanmak daha mantıklı olur mu?

Burada bir özü var FeedbackWidgetve JoinWidgetmevcut durumda . Benzer bir yapıya, benzer bir beginSendyönteme sahiptirler ve her ikisinin de bazı doğrulama desteğine sahip olması gerekecektir (henüz orada değil).


Bunun bir güncellemesi olarak - tepki, uzun gelecekte mixinleri destekleme konusunda ikinci düşüncelerimiz oluyor, çünkü örneğin componentDidMount'larınız sihirli bir şekilde çalışıyorsa, tepki bazı karmaşık şeyler yapıyor, bu yüzden birbirlerini aşırı yazmıyorlar .. çünkü mixinler çok basit ve amaca uygun değil
Dominic

React ile o kadar fazla deneyimim yok, ancak kendi karışımınızı gerçek React nesnelerinin ad alanıyla çakışmayan fonksiyonlarla tanımlayabilirsiniz. daha sonra tipik React bileşenleri işlevlerinizden "süper sınıf" / kompozisyon nesne işlevlerini çağırın. React fonksiyonları ile kalıtsal fonksiyonlar arasında örtüşme olmaz. bu, bir miktar standart metnin azaltılmasına yardımcı olur, ancak olan büyüyü sınırlar ve React'in sahne arkasında çalışmasını kolaylaştırır. bunu anlamak gerçekten o kadar zor mu? Umarım kendimi netleştirmişimdir.
Alexander Mills

Mixinler asla ölmeyecek, çünkü her zaman sadece DIY miksleri yapabilirsiniz. React, miksler için "yerel" desteğe sahip olmayacak, ancak yine de yerel JS ile miksler yapabilirsiniz.
Alexander Mills

Yanıtlar:


109

Güncelleme: bu cevap güncel değil. Mümkünse karışımlardan uzak durun. Seni uyardım!
Mixins öldü. Yaşasın Kompozisyon

İlk başta, bu ve özü için alt bileşenlerini kullanmaya çalıştı FormWidgetve InputWidget. Ancak, bu yaklaşımı yarıda bıraktım çünkü üretilen inputlerin ve onların durumlarının daha iyi kontrol edilmesini istiyordum .

Bana en çok yardımcı olan iki makale:

Sadece iki (farklı) mix yazmam gerektiği ortaya çıktı: ValidationMixinve FormMixin.
İşte onları nasıl ayırdım.

ValidationMixin

Doğrulama karışımı, doğrulayıcı işlevlerinizi eyaletinizin özelliklerinden bazıları üzerinde çalıştırmak ve "hatalı" özellikleri bir state.errorsdizide depolamak için kolaylık yöntemleri ekler, böylece ilgili alanları vurgulayabilirsiniz.

Kaynak ( özet )

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;

});

kullanım

ValidationMixinüç yöntem vardır: validate, hasErrorve resetError.
Şuna validatorsbenzer şekilde sınıfın nesneyi tanımlamasını bekler propTypes:

var JoinWidget = React.createClass({
  mixins: [React.addons.LinkedStateMixin, ValidationMixin, FormMixin],

  validators: {
    email: Misc.isValidEmail,
    name: function (name) {
      return name.length > 0;
    }
  },

  // ...

});

Kullanıcı gönderme düğmesine bastığında ararım validate. Bir çağrı, validateher doğrulayıcıyı çalıştırır ve this.state.errorsdoğrulaması başarısız olan özelliklerin anahtarlarını içeren bir diziyle doldurulur .

Benim içinde renderyöntemle, kullandığım hasErroralanlar için doğru CSS sınıfı oluşturmak için. Kullanıcı alana odaklandığında, bir resetErrorsonraki validateçağrıya kadar hata vurgulamasını kaldırmak için çağrıda bulunuyorum.

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

Form mixin, form durumunu işler (düzenlenebilir, gönderiliyor, gönderiliyor). Bunu, istek gönderilirken girişleri ve düğmeleri devre dışı bırakmak ve gönderildiğinde görünümünüzü buna göre güncellemek için kullanabilirsiniz.

Kaynak ( özet )

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;

});

kullanım

Bileşenin bir yöntem sağlamasını bekler: sendRequestbir Bluebird vaadi vermelidir. (Q ya da diğer sözler kitaplığı ile çalışması için onu değiştirmek önemsizdir.)

Bu gibi kolaylık yöntemler sağlar isFormEditable, isFormSubmittingve isFormSubmitted. Ayrıca istek başlaması bir yöntem sağlar: submitForm. Form düğmelerinin onClickişleyicisinden çağırabilirsiniz .


2
@jmcejuela Aslında daha sonra bileşen-ish yaklaşımına geçtim (hala yoğun olarak mixins kullanıyorum), bir noktada bunu genişletebilirim ..
Dan Abramov

1
"Daha bileşenli yaklaşım" hakkında bazı haberler var mı?
NilColor

3
@NilColor Henüz değil, ondan pek memnun kalmadım. :-) Şu anda FormInputsahibiyle bu görüşmelerim var formLink. formLinkgibidir valueLinkve döndürülen FormMixinbireyin linkValidatedState(name, validator)yöntem. FormInputonun değerini alır formLink.valuearamalar ve formLink.requestBlurve formLink.requestFocusde-onlar neden doğrulama FormMixin. Son olarak, giriş için kullanılan gerçek bileşeni özelleştirmek için onu şu FormInput<FormInput component={React.DOM.textarea} ... />
adrese

Güzel cevap - bazı ipuçları: donebluebird'ü aramanıza gerek yok ve kod Q'daki (veya yerel sözler) olduğu gibi çalışacak - tabii ki bluebird daha iyidir. Ayrıca sözdiziminin yanıttan bu yana React'te değiştiğine dikkat edin.
Benjamin Gruenbaum

4

React ile bir SPA inşa ediyorum (1 yıldan beri üretimde) ve neredeyse hiç mixins kullanmıyorum.

Karışımlar için şu anda sahip olduğum tek kullanım alanı, React'in yaşam döngüsü yöntemlerini ( componentDidMountvb.) Kullanan davranışı paylaşmak istediğiniz zamandır . Bu sorun, Dan Abramov'un bağlantısında (veya ES6 sınıf kalıtımını kullanarak) bahsettiği Yüksek Dereceli Bileşenler tarafından çözüldü .

Mixinler ayrıca, React'in "gizli" bağlam özelliğini kullanarak çerçeve API'nin tüm bileşenlere açık olmasını sağlamak için çerçevelerde sıklıkla kullanılır . ES6 sınıfı kalıtımla artık buna ihtiyaç duyulmayacak.


Diğer çoğu zaman, miksinler kullanılır, ancak gerçekten gerekli değildir ve basit yardımcılarla değiştirilebilir.

Örneğin:

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

LinkedStateMixinSözdiziminin şöyle olması için kodu kolayca yeniden düzenleyebilirsiniz:

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

Herhangi bir büyük fark var mı?


Haklısın. Aslında LinkedStateMixin belgeleri aslında mixin olmadan nasıl yapılacağını açıklıyor. Bu özel karışım gerçekten sadece biraz sözdizimsel şekerdir.
nextgentech
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.