[javascript] Facebook React에서 코드 재사용을 위해 믹스 인 대 구성 요소 사용

저는 Backbone 프로젝트에서 Facebook React를 사용하기 시작했으며 지금까지 정말 잘 진행되고 있습니다.
그러나 React 코드에 중복이 발생하는 것을 발견했습니다.

예를 들어, 나는 몇 가지 형태와 같은 위젯이 같은 상태로를 INITIAL, SENDING하고 SENT. 버튼을 누르면 양식의 유효성을 검사하고 요청한 다음 상태를 업데이트해야합니다. 상태는 this.state물론 필드 값과 함께 React 내부에 보관 됩니다.

이것이 Backbone 뷰라면,라는 기본 클래스를 추출했을 FormView것이지만 React는 뷰 로직을 공유하기 위해 서브 클래 싱을 지원하지도 지원하지도 않는다는 인상을 받았습니다 .

React에서 코드 재사용에 대한 두 가지 접근 방식을 보았습니다.

믹스 인과 컨테이너가 React의 상속보다 선호된다는 것이 맞습니까? 이것은 의도적 인 디자인 결정입니까? 두 번째 단락의 “양식 위젯”예제에 mixin 또는 컨테이너 구성 요소를 사용하는 것이 더 합리적입니까?

여기에 요지입니다 FeedbackWidgetJoinWidget현재 상태는 . 그들은 유사한 구조, 유사한 beginSend방법을 가지고 있으며 둘 다 일부 유효성 검사 지원이 필요합니다 (아직 존재하지 않음).



답변

업데이트 :이 답변은 구식입니다. 가능하면 믹스 인을 멀리하십시오. 경고 했어!
Mixins는 죽었습니다. 긴 라이브 작곡

처음에는이를 위해 하위 구성 요소를 사용 FormWidget하고 InputWidget. 그러나 생성 된 inputs와 그 상태에 대한 더 나은 제어를 원했기 때문에이 접근 방식을 중간에 포기했습니다 .

가장 도움이 된 두 가지 기사 :

내가 두 (다른)이나 mixin을 쓸 필요가 있다고 밝혀졌다 : ValidationMixinFormMixin.
내가 그들을 분리 한 방법은 다음과 같습니다.

유효성 검사

유효성 검사 믹스 인은 상태의 일부 속성에서 유효성 검사기 함수를 실행하고 “오류가 발생한”속성을 state.errors배열 에 저장하는 편리한 메서드를 추가하여 해당 필드를 강조 표시 할 수 있습니다.

출처 ( 요점 )

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;

});

용법

ValidationMixin세 가지 방법이 있습니다 validate, hasError그리고 resetError.
다음 validators과 같이 클래스가 객체 를 정의 할 것으로 예상 합니다 propTypes.

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

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

  // ...

});

사용자가 제출 버튼을 누르면을 호출 validate합니다. 에 대한 호출 validate은 각 유효성 검사기를 실행하고 유효성 this.state.errors검사에 실패한 속성의 키를 포함하는 배열로 채 웁니다 .

render방법 에서는 hasError필드에 대한 올바른 CSS 클래스를 생성 하는 데 사용 합니다. 사용자가 필드 안에 포커스를 resetError두면 다음 validate호출 까지 오류 강조 표시를 제거하도록 호출합니다.

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

양식 믹스 인은 양식 상태 (편집 가능, 제출, 제출)를 처리합니다. 이를 사용하여 요청이 전송되는 동안 입력 및 버튼을 비활성화하고 전송시 해당 뷰를 업데이트 할 수 있습니다.

출처 ( 요점 )

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;

});

용법

컴포넌트가 sendRequestBluebird promise를 반환해야하는 하나의 메서드를 제공 할 것으로 예상합니다 . (Q 또는 다른 promise 라이브러리와 함께 작동하도록 수정하는 것은 간단합니다.)

이 같은 편리한 방법을 제공합니다 isFormEditable, isFormSubmitting하고 isFormSubmitted. 또한 요청을 시작하는 방법을 제공합니다 submitForm.. 폼 버튼의 onClick핸들러 에서 호출 할 수 있습니다 .


답변

React로 SPA를 구축하고 있으며 (1 년 이후 생산 중) 믹스 인을 거의 사용하지 않습니다.

현재 믹스 인에 대한 유일한 사용 사례는 React의 수명주기 메서드 ( componentDidMount등) 를 사용하는 동작을 공유하려는 경우 입니다. 이 문제는 Dan Abramov가 자신의 링크 에서 말하는 Higher-Order Components (또는 ES6 클래스 상속 사용)로 해결됩니다.

Mixins는 React 의 “숨겨진” 컨텍스트 기능 을 사용하여 모든 구성 요소에서 프레임 워크 API를 사용할 수 있도록 프레임 워크에서 자주 사용됩니다 . 이것은 ES6 클래스 상속에서도 더 이상 필요하지 않습니다.


대부분의 경우 믹스 인이 사용되지만 실제로는 필요하지 않으며 간단한 도우미로 쉽게 대체 할 수 있습니다.

예를 들면 :

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

LinkedStateMixin구문이 다음과 같 도록 코드를 매우 쉽게 리팩터링 할 수 있습니다.

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

큰 차이가 있습니까?


답변