神は人々を強くし、弱くしました。 サミュエル・コルトは彼らを平等にした。
FluxとReduxはほぼ同じことをしました。
問題は1つだけでした-Reduxは本質的に非常に原始的なクソであり、少なくとも何らかの形でそれを使用するには、サンク、サーガ、オブザーバブルなどのミドルウェアを追加する必要がありました。
しかし、この記事は反対側から来ており、構成、特にコンポーネントモデルについて真剣に尋ねています。 エディタで-いいえ。
問題をよりよく理解するために、基本から始めて、TODOを作成しましょう。
反応オプション
誰もがコンポーネントモデルに対する反応を愛し、一般的にそれが非常に「構成可能」であるという事実のために反応します。 ほぼすべてのコンポーネントを使用して、他の10個のコンポーネントにラップすれば、希望どおりに機能します。 (このフレーズを覚えておいてください)
// TODO const TODOs = [todo1, todo2, todo3]; // const Application = <TodoList todos={TODOs} /> // TodoList, const TodoList = ({todos}) => ( <ol> {todos.map( todo => <Todo key={todo.id} {...todo} /> </ol> ); // TODO const Todo = (props) => <div>......</div>
すべては問題ありませんが、イベントの処理方法、それをすべて制御する方法についての質問が発生します
シンプルなReduxオプション
Reduxは多くの問題を解決しますが、今からは説明しません。 以下のコードが完全に明確で誰にとっても明白であることを願っています。
// const store = createStore({ todos: [todo1, todo2, todo3] }); // const Application = <Provider store={store}> <ConnectedTodoList/> </Provider> // . TODO const ConnectedTodoList = connect( state => ({todos: state.todos}), { onTodoClick } )(TodoList) // const TodoList = ({todos}) => ( <ol> {todos.map( todo => <Todo key={todo.id} {...todo} onClick={() => onTodoClick(todo.id)} /> </ol> ); ....
これは悪い reduxアプリケーションの例であり、これはreduxリポジトリの元の例のようです。
2つの問題があります。
-ReactとReduxをミックスする
-Todoが変更されると、すべてが再レンダリングされます-Todoリストと各Todoの両方。
より正しいRedux
正しいバージョンと間違ったバージョンとの違いはありません。
// . TODO const ConnectedTodoList = connect( state => ({todos: getOnlyIds(state.todos)}), <----- { onTodoClick } )(TodoList) const TodoList = ({todos}) => ( <ol> {todos.map( id => <ConnectedTodo key={id} id={id} /> <----- </ol> ); // Todo const ConnectedTodo = connect( (state,props) => ({...state.todos[props.id])}), <----- { onTodoClick: () => dispatch => dispatch(onTodoClick(props.id)) } )(Todo) ....
何が良いですか? TodoListは特定のTodoのコンテンツに依存せず、可用性の事実にのみ応答します。 さて、Todo-彼はデータを取得し、onTodoClickに彼のIDを追加します。
実際、「より多くの」エディターを追加することはできなくなりました。 同時に、親から子にデータを渡すためのReactメカニズムは使用されなくなりました。 結果として、もはや合成は不可能です。
どんな構成ですか?
「問題」の良い例は、やはり公式リポジトリにあるTree-viewの例です。
技術的には-同じTODOリスト、「ネスト」のみが表示されます。 「格納」は完全にフラットであり、最も深いノードはIdでアドレス指定できるため、実際には「ネスト」はありません。 そして、問題はこのデータ構造にあるのではなく、別のデータ構造を使用するのは簡単なことです!
これは、ノード、子ノードをレンダリングする会議、ConnectedNodeのレンダリング、およびreduxの場合、すべての接続コンポーネントが同等であり、ストアに直接接続するためです。
また、 コンポーネントが等しい場合、コンポーネントはまったく同じアクションを実行する必要があります。既知のIDを持つ配列の要素を取得します。
言い換えると、コンポーネントはツリー内の位置に関係なく機能します。コンポーネントを取得して別の10にラップし、必要に応じて機能させることは絶対に不可能です。常に機能します。 予想通り-もちろんそうです。 欲しかったのですか?
Trite-すべてのTODOリストをリストリストに再作成してみてください、ala Trello-すべてについて、そして確かにすべての接続について書き直さなければなりません。
そして、問題がコンポーネントのネストの冗長性による完全な無視である場合-おそらくこれはあなたが修正する必要があるものです。 という意味で
やめて!
親コンポーネントが必要と考えるときに、子のデータを準備できる状況を想像してください。 親が子供が実行するすべてのアクションを制御できる状況を想像してください。 そして、これはreduxが「修正」しようとしたおおよそのものであり、これを洗い流すことはできないことを忘れないでください。
それは本当に少し違うことです。 問題は双方向のイベントであり、これにより制御がほとんど不可能なカスケード更新が発生しました。
さらに、例の99%は標準のReactaアプローチを引き続き使用します。これにより、フックまたは詐欺師が、秘密のIDを子供に投げて、ストアからデータを読み取ることができます。
それはもう少し簡単に、より正確に行うことができます。
Redux-restate
Redux-restate( github )は、1つ以上のストアに入るためのミニチュア(50行*)ライブラリであり、それらに対して特定の操作を実行し、終了するためにストアを発行します。
すぐに明らかにする価値があります-ストーリーの数は変わりません! restateは、データベース内のビュー、mobxへの変換、レンズ、またはmapStateToProps(ToStateのみ)です。 これは保管ではありません。
実際、restateは3つの個別のパッケージです。
-redux-restate-下位レベル、必要なことを行う
-react-redux-restate-reactの周りのバインディング-コンテキストからストレージを取得し、元に戻します(狭いポイント)
-react-redux-focus-restateの簡易バージョン。 ケースの99%で使用します。
すべてが単純に機能します:
-「没入」(状態の作成)時に、子供が利用できるデータをプログラムで変更できます。
-「浮上」(イベント処理)する場合、最終チームを正しく形成するために必要なデータでイベントを補足できます。
「イマージョン」は、入力に状態を取得し、その状態を終了する一方向のアクションです。 mapStateToPropsのように、同じ再構成で中間結果をメモすることが非常に重要です。
const composeState = (state, props) => ({ ...state, part: recompose(operationOn(state)) });
昇順は、サブコンポーネントがディスパッチを呼び出すときに実行される単方向アクションです。 その意味は、状態の作成時に削除された「特異性」を追加することです。
const routeDispatch = (dispatch, event, props) => dispatch({...event, somethingFrom: props.data });
「さらに正しい」リデュースの例:
// . TODO const ConnectedTodoList = connect( state => ({todos: getOnlyIds(state.todos)}), { onTodoClick } )(TodoList) const TodoList = ({todos}) => ( <ol> {todos.map( id => <RestatedTodo key={id} id={id} /> <----- </ol> ); // Todo (reactReduxFocus - API ) const RestatedTodo = reactReduxFocus( (state, props) => ({todo: state.todos[props.id]}), // __ todo (dispatch, event, props) => dispatch({...event, id: props.id}); // todoID )(ConnectedTodo) // Todo const ConnectedTodo = connect( (state,props) => ({...state.todo}), <----- { onTodoClick } <---- )(Todo) ....
副作用として、自動areStatesEqualを取得します-計算されたshallowEqual状態が古い場合、変更の伝播が停止し、アプリケーションのさまざまな部分を相互に分離できます。
ツリーの例は「もっと」正しく書けるようになりました
const RestatedNode = reactReduxFocus( (state, props) => ({ ...state, // node node: state.node.children[props.nodeId] }), (dispatch, event, props) => dispatch({ ...event, // nodeId nodeId:[props.nodeId, ...event.nodeId] }); )(ConnectedNode); const ConnectedNode = connect( state => { ...state, children: state.node.children, leafs: state.node.leafs }........); )(Node)
そして、それがすべてです-RestatedNodeを相互に挿入します-reactReduxFocusは最初にすべてを慎重に解析し、すべてのブレッドクラムを収集してコマンドディスパッチに戻します。
問題は、「合成」、「何もない」がもはや必要なくなったときに、どのように通常に戻るかということです。 まあ、反応ごとに複数のストアを必要とするreactReduxRestateが必要です。
import {createProvider} from 'react-redux' import reactReduxFocus from 'react-redux-focus' import reactReduxRestate from 'react-redux-restate' const Provider = createProvider('realStore'); // restate 'realStore', - 'store', connect const FocusedComponent = reactReduxFocus(..., ..., { storageKey: 'realStore' })(SomeComponent); const RestatedComponent = reactReduxRestate({ // default ('store') 'realStore' // , . - ? realStore: 'realStore' },..., ...)(AnotherComponent) <Provider store={realStore}> <FocusedComponent > <RestatedComponent /> - "" <FocusedComponent /> - , , </FocusedComponent </Provider>
一般に、3つのコンポーネントすべてのソースコードは実際には数十行かかりますが、...合成のための非常に多くのオプションを開きます。
原則として、これはまさに要求されたものです。
そしてそれは可能でしたか?
Restateは、この問題の先駆者ではありません。 たとえば、 electron-reduxは「ほぼ」同じことを行います。2つのストア(メインとレンダー)を接続します。1つは実際のものではなく、ルートをディスパッチします。
技術的には、松葉杖の数を減らしてrestateを使用しても同じ効果が得られますが、restateにはIPCはまったくありません。
または、「オプションシステム」Yandex.Mapsを使用します。 一般的にはあまり知られていませんが(パブリックおよびドキュメント化されています)、Yandex.Maps APIを少なくとも一度使用した人は、そこでオプションが美しく「カスケード」していることに気づくことができます-ポリラインの色はマップレベルで設定され、オプションはgeoObjectStrokeColorと呼ばれ、ジオオブジェクトレベルで-strokeColor。
一般に、「プレフィックス」は機能し、どこでも見つけることができます-作業の最良の例- カードを設定する例では、 「scrollZoomSpeed」が設定され、制御コンポーネント自体は単にストアから「速度」を読み取ります。
ただし、Yandex.Mapsでのみ、このプレフィックスは下から上に機能します。一致するまでプレフィックスが子から親に追加されます。これにより、更新を停止する可能性がほとんどなくなります。トリッキーな方法でこの値を読み取るコンポーネントを見つけることはできません
再記述-Yandex.Mapsシステムとreduxを正確に横断する試み。 両方を修復します。 まあ、長い休暇の夜を取る。
-> github.com/thekashey/restate