RxJでReactJSコンポーネントを簡素化する

はじめに



おそらく、これら2つのライブラリを十分に試した多くの人々が、それらを生産的に一緒に使用する方法を考えていました。 RxJ自体は単純ではありません。多くの機能は間違いなく初心者をはじきます。 しかし、それを研究して受け入れたため、非同期コードを操作するための非常に柔軟なツールが手に入りました。



この投稿を読むことで、ReactJSをよく知っており、少なくともRxJの本質を理解しているということです。 例ではReduxを使用しませんが、以下に記述されるものはすべて、React + Reduxバンドルに完全に投影されます。



やる気



そのprops



に対していくつかの非同期/重いアクション(それらを「リカウント」と呼びましょう)を実行し、実行結果を表示するコンポーネントがあります。 一般的に、3種類のprops



ます:



  1. 再カウントおよびレンダリングする必要がある変更時のパラメーター
  2. 前回の再カウントおよびレンダリングの値を使用する必要がある変更時のパラメーター
  3. 変更が翻訳またはレンダリングを必要としないパラメーター、ただし、次の変換に影響を与える


不要な移動を行わず、必要な場合にのみ再カウントしてレンダリングすることが非常に重要です。 たとえば、渡されたパラメーターに従って、フィボナッチ数をカウントおよび表示するコンポーネントを考えます。 次の入力があります。



  1. className



    ルート要素にハングアップするCSSクラス(タイプ2)
  2. value



    計算に使用される数値(第1タイプ)
  3. useServerCall



    サーバーへの要求を介して、またはローカルで計算できるようにするパラメーター(3番目のタイプ)


コンポーネントの例
 import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; //,     Promise import calculateFibonacciExternal from './calculateFibonacci'; export default class Fibonacci extends React.Component { //  ,   static propTypes = { className: PropTypes.string, value: PropTypes.number.isRequired, useServerCall: PropTypes.bool.isRequired, }; //  .      //  state = { loading: true, fibonacci: null, }; //    componentWillMount() { //       -   // ,    this.calculateFibonacci(this.props.value, this.props.useServerCall, (fibonacci) => { this.setState({ fibonacci: fibonacci, loading: false, }); }); } //   props componentWillReceiveProps(nextProps) { //  value -   if(nextProps.value !== this.props.value) { this.setState({ loading: true, }); this.calculateFibonacci(nextProps.value, nextProps.useServerCall, (fibonacci) => { this.setState({ fibonacci: fibonacci, loading: false, }); }); } } //    shouldComponentUpdate(nextProps, nextState) { //      ,   useServerCall return this.props.className !== nextProps.className || this.props.value !== nextProps.value || this.state.loading !== nextState.loading || this.state.fibonacci !== nextState.fibonacci; } // ,          //  ,     componentWillUnmount() { this.unmounted = true; } unmounted = false; calculationId = 0; //      ,   //     calculateFibonacci = (value, useServerCall, cb) => { const currentCalculationId = ++this.calculationId; calculateFibonacciExternal(value, useServerCall).then(fibonacci => { if(currentCalculationId === this.calculationId && !this.unmounted) { cb(fibonacci); } }); }; //    render() { return ( <div className={ classnames(this.props.className, this.state.loading && 'loading') }> { this.state.loading ? 'Loading...' : `Fibonacci of ${this.props.value} = ${this.state.fibonacci}` } </div> ); } }
      
      







コード全体がコンポーネントのライフサイクルの4つの方法、接続されているように見える場所、以前の状態との比較に従って分散されているため、更新時に何かを簡単に忘れたり壊したりします。 このコードを改善してみましょう。



react-rx-propsの紹介



この問題の解決をより簡潔にするために、この小さなライブラリを作成しました。 2つの高次コンポーネント(HoC、高次コンポーネント)で構成されます。



  1. reactRxProps



    受信props



    (いくつかの例外を除く)をObservableに変換し、コンポーネントに渡します
  2. reactRxPropsConnect



    コンポーネントからObservablesを操作するロジックを使用して、内部状態なしで(ステートレス)作成できるようにします


最初のHoCを使用すると、次のものが得られます。



reactRxPropsを使用したコンポーネントの例
 import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { reactRxProps } from 'react-rx-props'; import { Observable } from 'rxjs'; import calculateFibonacciExternal from './calculateFibonacci'; //  Promise  Observable  . const calculateFibonacci = (...args) => Observable.fromPromise(calculateFibonacciExternal(...args)); class FibonacciReactRxProps extends React.Component { // ,     Observables //$        ( ) static propTypes = { className: PropTypes.string, value$: PropTypes.instanceOf(Observable).isRequired, useServerCall$: PropTypes.instanceOf(Observable).isRequired, exist$: PropTypes.instanceOf(Observable).isRequired, }; //       state = { loading: true, fibonacci: null, }; //            componentWillMount() { //useServerCall   ,       this.props.useServerCall$.subscribe(useServerCall => { this.useServerCall = useServerCall; }); //value         this.props.value$.switchMap(value => { this.value = value; this.setState({ loading: true, }); return calculateFibonacci(value, this.useServerCall) .takeUntil(this.props.exist$); //       }).subscribe(fibonacci => { this.setState({ loading: false, fibonacci: fibonacci, }); }); //     className,     propTypes -    //Observable.       . //  props,      Observables } //    render() { return ( <div className={ classnames(this.props.className, this.state.loading && 'loading') }> { this.state.loading ? 'Loading...' : `Fibonacci of ${this.value} = ${this.state.fibonacci}` } </div> ); } } // HoC,     (  ) export default reactRxProps({ propTypes: { className: PropTypes.string, value: PropTypes.number.isRequired, useServerCall: PropTypes.bool.isRequired, }, })(FibonacciReactRxProps);
      
      







元のコンポーネントと比較した利点は何ですか:



  1. 再カウントとレンダリングを1か所で行うタイミングに関するすべてのロジック
  2. 重複コードなし
  3. 以前の状態比較はありません
  4. takeUntil(this.props.exist$)



    を使用して、Observableの登録をいつでも自動的に解除できます。
  5. 無関係な計算結果を必要としないロジック全体は、 switchMap



    の起動にありswitchMap





ただし、コンポーネントにはまだ内部状態があり、テストが複雑になります。 2番目のHoCを使用しましょう:



内部状態のないコンポーネントの例
 import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; import { reactRxProps, reactRxPropsConnect } from 'react-rx-props'; import { compose } from 'recompose'; import { Observable } from 'rxjs'; import calculateFibonacciExternal from './calculateFibonacci'; const calculateFibonacci = (...args) => Observable.fromPromise(calculateFibonacciExternal(...args)); class FibonacciReactRxProps extends React.Component { //           //     static propTypes = { className: PropTypes.string, value: PropTypes.number, fibonacci: PropTypes.number, }; //,  render() { return ( <div className={ classnames(this.props.className, this.props.loading && 'loading') }> { this.props.loading ? 'Loading...' : `Fibonacci of ${this.props.value} = ${this.props.fibonacci}` } </div> ); } } //compose    HoC    export default compose( //     reactRxProps({ propTypes: { className: PropTypes.string, value: PropTypes.number.isRequired, useServerCall: PropTypes.bool.isRequired, }, }), reactRxPropsConnect({ //   props       propTypes: { className: PropTypes.string, value$: PropTypes.instanceOf(Observable).isRequired, useServerCall$: PropTypes.instanceOf(Observable).isRequired, exist$: PropTypes.instanceOf(Observable).isRequired, }, //      Observables //    , : //this -> model //this.props -> props //this.setState -> render connect: (props, render) => { const model = {}; props.useServerCall$.subscribe(useServerCall => { model.useServerCall = useServerCall; }); props.value$.switchMap(value => { model.value = value; render({ loading: true, }); return calculateFibonacci(model.value, model.useServerCall) .takeUntil(props.exist$); }).subscribe(fibonacci => { render({ loading: false, value: model.value, fibonacci: fibonacci, }); }); }, }) )(FibonacciReactRxProps);
      
      







コンポーネントは、Observablesに関連するすべてのロジックと同様に、内部状態を失い、新しい接続機能と同様に、テストの基本になりました。



このアプローチを楽しんで、あなたもそれを試してみることにしたと思います。 この機能を備えたライブラリを見つけようとしましたが、残念ながら、検索で結果が返されませんでした。



参照:



React Rx Propsライブラリ

ライブラリの例



All Articles