はじめに
おそらく、これら2つのライブラリを十分に試した多くの人々が、それらを生産的に一緒に使用する方法を考えていました。 RxJ自体は単純ではありません。多くの機能は間違いなく初心者をはじきます。 しかし、それを研究して受け入れたため、非同期コードを操作するための非常に柔軟なツールが手に入りました。
この投稿を読むことで、ReactJSをよく知っており、少なくともRxJの本質を理解しているということです。 例ではReduxを使用しませんが、以下に記述されるものはすべて、React + Reduxバンドルに完全に投影されます。
やる気
その
props
に対していくつかの非同期/重いアクション(それらを「リカウント」と呼びましょう)を実行し、実行結果を表示するコンポーネントがあります。 一般的に、3種類の
props
ます:
- 再カウントおよびレンダリングする必要がある変更時のパラメーター
- 前回の再カウントおよびレンダリングの値を使用する必要がある変更時のパラメーター
- 変更が翻訳またはレンダリングを必要としないパラメーター、ただし、次の変換に影響を与える
不要な移動を行わず、必要な場合にのみ再カウントしてレンダリングすることが非常に重要です。 たとえば、渡されたパラメーターに従って、フィボナッチ数をカウントおよび表示するコンポーネントを考えます。 次の入力があります。
-
className
ルート要素にハングアップするCSSクラス(タイプ2) -
value
計算に使用される数値(第1タイプ) -
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、高次コンポーネント)で構成されます。
-
reactRxProps
受信props
(いくつかの例外を除く)をObservableに変換し、コンポーネントに渡します -
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か所で行うタイミングに関するすべてのロジック
- 重複コードなし
- 以前の状態比較はありません
-
takeUntil(this.props.exist$)
を使用して、Observableの登録をいつでも自動的に解除できます。 - 無関係な計算結果を必要としないロジック全体は、
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ライブラリ
ライブラリの例