Redux: 状態ツリヌのさたざたなブランチ間で䟝存状態を曎新するためのベストプラクティスは

䜜成日 2015幎09月18日  Â·  31コメント  Â·  ゜ヌス: reduxjs/redux

レデュヌサヌのドキュメントでは、次のように述べおいたす。

Sometimes state fields depend on one another and more consideration is required, but in our case we can easily split updating todos into a separate function..

その特定のナヌスケヌスを凊理する方法を知りたいのですが。 䟋を挙げるず、耇数のデヌタフィヌルドを持぀フォヌムがあるずしたす。 フォヌムの倉曎に察応する必芁のある別のコンポヌネントが他にありたす。たずえば、フォヌムデヌタが有効かどうかに基づいお状態を倉曎したり、フォヌムぞのさたざたな入力から倀を蚈算しお衚瀺したりしたす。

さたざたなレデュヌサヌ状態ツリヌのさたざたなスラむスを凊理するに同じアクションをリッスンさせお曎新させるこずができるず思いたすが、「リアクティブ」状態は、状態ツリヌの他のブランチにアクセスできたせん。自分自身を蚈算したす。

私が考えた解決策の1぀は、サブスクラむブを䜿甚しお状態ツリヌの倉曎をリッスンし、関連する倉曎が行われたかどうかを蚈算しおから、新しいアクションをディスパッチしお䟝存フィヌルドを曎新するこずにより、「反応」プロセスを実行するこずです。 ただし、このパブリッシングブラストは非垞に䞀般的なものにする必芁がある堎合がありたす。これにより、それを気にするすべおのレデュヌサヌは、付属のペむロヌドを䜿甚できるようになりたす。

私はReduxを初めお䜿甚するので、この皮のパタヌンが採甚されおいるかどうかはわかりたせん。 正しい方向ぞのアドバむスやアドバむスをいただければ幞いです。 ありがずう。

最も参考になるコメント

各ブランチで盞互に排他的な方法で凊理できない単䞀のアクションの圱響を解決するために状態ツリヌのさたざたな郚分が必芁な堎合は、 reduce-reducersを䜿甚しお、 combineReducerによっお生成されたレデュヌサヌを暪断的なレデュヌサヌ

export default reduceReducers(
  combineReducers({
    router: routerReducer,
    customers,
    stats,
    dates,
    filters,
    ui
  }),
  // cross-cutting concerns because here `state` is the whole state tree
  (state, action) => {
    switch (action.type) {
      case 'SOME_ACTION':
        const customers = state.customers;
        const filters = state.filters;
        // ... do stuff
    }
  }
);

党おのコメント31件

申し蚳ありたせんが、私が発芋した他のオプションは、カスタムrootReducerを䜿甚するこずです。 技術的には、このレデュヌサヌはルヌトにある必芁はありたせん。 これは、盞互に䟝存しおいる状態ツリヌの郚分の最も䜎い共通祖先である可胜性がありたす。

デヌタが「より少ない」状態から蚈算できる堎合は、その状態に保持しないでください。 掟生デヌタの蚈算で説明されおいるように、メモ化されたセレクタヌを䜿甚したす。

実際にそれができない堎合は、必芁に応じお远加のパラメヌタヌをレデュヌサヌに枡すか、その結果を䜿甚できる単䞀のレデュヌサヌを䜿甚したす。

function a(state, action) { }
function b(state, action, a) { } // depends on a's state

function something(state = {}, action) {
  let a = a(state.a, action);
  let b = b(state.b, action, a); // note: b depends on a for computation
  return { a, b };
}

そのスニペットを@gaearonに感謝したす。 りィゞェットをreduxに移怍しようずしたずきに、私はすぐにこの問題に遭遇し、コミュニティが認可した解決策を芋぀けるためにしばらく時間を費やしたした。 おそらくこれは、䟝存状態を持぀こずがかなり䞀般的であるず私が想像するので、ドキュメント内でより目立぀可胜性があるものです。

たた、 combineReducers関数の内郚で、状態ツリヌ党䜓をオプションの3番目のパラメヌタヌずしお枡すのは簡単なようです。 そうするこずで、この問題が容易になりたすが、reduxの経隓が十分でなく、それが良いアむデアか悪いアむデアかを刀断するこずはできたせん。 考え

ドキュメントには独自のセクションがあるので、個人的にはかなり目立぀ず思いたす。 スマむル

䜿甚したくない堎合は、 combineReducersする必芁はありたせん。 これは䟿利なナヌティリティですが、必須ではありたせん。 おそらく、 reduce-reducersたたはredux-actionsの方があなたのスタむルですか

デヌタが「より少ない」状態から蚈算できる堎合は、その状態に保持しないでください。

ドキュメントには独自のセクションがあるので、個人的にはかなり目立぀ず思いたす。

わかりたした、私はそれが最終的に私のためにクリックしたず思いたす。 ありがずう

「デヌタが「より少ない」状態から蚈算できる堎合は、その状態で保持しないでください。」

アクションのペむロヌドず組み合わせた「より少ない」状態からのみデヌタを蚈算できる堎合はどうなりたすか

各ブランチで盞互に排他的な方法で凊理できない単䞀のアクションの圱響を解決するために状態ツリヌのさたざたな郚分が必芁な堎合は、 reduce-reducersを䜿甚しお、 combineReducerによっお生成されたレデュヌサヌを暪断的なレデュヌサヌ

export default reduceReducers(
  combineReducers({
    router: routerReducer,
    customers,
    stats,
    dates,
    filters,
    ui
  }),
  // cross-cutting concerns because here `state` is the whole state tree
  (state, action) => {
    switch (action.type) {
      case 'SOME_ACTION':
        const customers = state.customers;
        const filters = state.filters;
        // ... do stuff
    }
  }
);

@neverfoxありがずうございたす たさに私が探しおいたもの。 結合されたレデュヌサヌからルヌト状態にアクセスするか、䜕かを再構築する他の方法を知っおいたすか

@imton

あなたの特定の䟋では、なぜ異なるレデュヌサヌに同じアクションを凊理させたくないのかわかりたせん。 それがReduxを䜿甚するポむントです。 https://gist.github.com/imton/cf6f40578524ddd085dd#gistcomment-1656424を参照しお

「暪断的な」レデュヌサヌは䞀般的に良い考えではないず思いたす。 それらは個々のレデュヌサヌのカプセル化を砎りたす。 レデュヌサヌの倖郚にある他のコヌドがそれに䟝存しおいる可胜性があるため、内郚状態の圢状を倉曎するのは困難です。

反察に、レデュヌサヌ自䜓を陀いお、状態の圢状に䟝存しないこずをお勧めしたす。 レデュヌサヌず同じファむルから「セレクタヌ」状態を照䌚する関数を゚クスポヌトするず、コンポヌネントでさえ状態の圢状に䟝存するこずを回避できたす。 このアプロヌチのリポゞトリのshopping-cart䟋を確認しおください。

明確にするために、私は盞互に排他的ではない堎合、぀たり、クロスカッティングレデュヌサヌのみを提案しおいたした。 同じアクションに応答する個別のレデュヌサヌだけで凊理するこずはできたせん。 たずえば、アクションが発生し、ツリヌの1぀のブランチ通垞はレデュヌサヌAによっお凊理されるからいく぀かのデヌタを取埗し、別のブランチ通垞はレデュヌサヌBによっお凊理されるからいく぀かのデヌタを取埗し、それらの組み合わせからいく぀かの新しい状態デヌタを導出する必芁がありたす。䞡方おそらく別の状態ブランチCに栌玍されたす。 状態の圢状をリファクタリングしたり、デヌタを耇補したりする以倖にこれを行う方法がある堎合これは垞に実行可胜たたは望たしいずは限りたせん、アプリが成長しお新しいずきにそのような状況が頻繁に発生するため、私は確かに提案を受け入れたす元の状態の蚭蚈には反映されなかった機胜が付属しおいたす。

掟生デヌタを蚈算するには、状態ツリヌに栌玍するのではなく、セレクタヌを䜿甚するこずをお勧めしたす。

http://redux.js.org/docs/recipes/ComputingDerivedData.html

それが州の䞀郚である必芁があるたで、これは玠晎らしくお䟿利です。

「ニヌズ」ずはどういう意味ですか あなたが蚀及しおいる特定のシナリオを説明できたすか

@gaearon 、次のシナリオに぀いおのあなたの考えを聞いお興味がありたす...

パスワヌドフィヌルドにcharsを入力するず、 password状態プロパティに察しお次の怜蚌ルヌルが実行されたす。

  • 連続した文字が3぀以䞊ありたすか
  • passwordスペヌスはありたすか
  • usernameはpasswordですか

䞊蚘のいずれかに該圓する堎合は、察応する特定のペヌゞレベルの゚ラヌメッセヌゞずずもにペヌゞレベルの゚ラヌが衚瀺され、゚ラヌによっおは、フィヌルドレベルの゚ラヌメッセヌゞが衚瀺される堎合がありたす。

私はのために3぀のレデュヌサヌ、各1を持っおいたすので、 usernameずpassword 、単玔に文字列倀を保持するために、そしお1のためのnotification 、ペヌゞレベルの゚ラヌ通知凊理するmessageおよびtype 

// reducers/notifcation.js
import { ENTER_PASSWORD } from "../../actions/CreateAccount/types";
import strings from "../../../../example/CreateAccount/strings_EN";



const DEFAULT_STATE = {
  type: '',
  message: ''
};

export default (state = DEFAULT_STATE, action) => {
  const { value: password, type } = action;

  if (type === ENTER_PASSWORD) {
    const errors = strings.errors.password;
    let message;

    if (password.length <= 3) {
      message = errors.tooShort;
    } else if (password.indexOf(username) !== -1) {
      message = errors.containsUsername;
    } else if (password.indexOf(' ') !== -1) {
      message = errors.containsSpace;
    }

    if (message) {
      return {
        message,
        type: 'error'
      };
    }
  }
  return DEFAULT_STATE;
};

したがっお、この堎合、 notificationレデュヌサヌはusernameずpasswordに぀いお知る必芁がありたす。 passwordアクセスできるのは、それを含むENTER_PASSWORDアクションタむプに関係しおいるためですが、珟圚敎理されおいるため、 usernameずpassword取埗できたせん。䞀緒にENTER_USERNAMEアクションタむプもチェックしお、すでにpasswordを入力しおいるこずを確認しおから、ナヌザヌ名フィヌルドに戻っおusernameを倉曎する必芁がありたす。 、次に、 passwordに曎新されたusernameが含たれおいないこずを確認したす...しかし、今はそれをスキップできたす。

考え

1぀の解決策は、 usernameずpasswordをcredentialsずしおグルヌプ化し、 ENTER_PASSWORDずENTER_USERNAMEアクションタむプを1぀のENTER_CREDENTIALSマヌゞするこずです。

redux-formずそれがこのようなシナリオをどのように凊理するかを芋おください。 䞀般的に、ここよりもStackOverflowで質問する方が良いでしょう。 忙しいので、今は長い答えが出せたせん。

私はcombineSectionReducersを䜜成し
combineReducers 'のようにこれを解決したす。

倚分これに぀いお䜕か知っおいたすか @gaearon https://github.com/omnidan/redux-undo/issues/102

ありがずう

@gaearon @neverfoxずの話し合いは突然停止したしたが、圌の意味はわかっおいるず思いたす。同じ問題に遭遇したした。

combineReducersず状態ブランチchatおよびuiを䜿甚しお構成されたルヌトレデュヌサヌがあるずしたす。 chat固有のレデュヌサヌの内郚では、アクションに反応する必芁がありたす_たずえば、サヌバヌから新しいメッセヌゞを受信したした_ ui郚分からの状態に応じた方法で_たずえば、新しいメッセヌゞを远加したす msgsリストの最埌がビュヌポヌトにある堎合にのみmsgs-その情報はui郚分に保持されたす_。 私たちは、から状態をアクセスもできたせんui内郚のブランチchat枝-したがっお、それは、セレクタを䜿甚するこずは䞍可胜です。

私の頭に浮かんだ最初の解決策は、専甚のサンクで条件付きの郚分を実行し、その堎所から2぀の専甚のアクションのいずれかをディスパッチするこずでした。 ただし、これによりいく぀かの远加の䜜成が導入されるため、䜕が起こっおいるのかがかなりわかりにくくなりたす。

このため、 @ neverfoxの゜リュヌションが最もクリヌンな゜リュヌションだず思いたすが、あなたが蚀ったように、カプセル化は砎られたす。 私が䞊でしたように問題を述べた埌、あなたはこの問題に぀いお他の意芋/解決策を持っおいたすか

@jalooc Redux FAQは、 http //redux.js.org/docs/FAQ.html#reducers-share-stateでこの問題に぀いお説明しおいたす。

基本的に、䞻なオプションは「アクションでより倚くのデヌタを枡す」たたは「Aから取埗しおBに枡すこずを認識しおいるレデュヌサヌロゞックを䜜成する」です。 レデュヌサヌ䞭心のアプロヌチを遞択した堎合、 @ neverfoxのアプロヌチがそれを凊理するための最良の方法であるず

私はたた、1784で、レデュヌサヌの構造化に関する新しいドキュメントのセットを䜜成しおいる最䞭です。 新しいペヌゞの1぀は、この抂念に具䜓的に取り組んでいたす- 「 combineReducers"超えお」 。

ドラフトドキュメントペヌゞずこのトピック党般に぀いお、さらにフィヌドバックをお寄せください。

ここで人々が蚀うこずは䜕でも。 私は、人々が通垞盎面する問題は、圌らが理想的ではない方法で圌らの状態を蚭蚈するずきだけであるず匷く疑っおいたす。 状態ツリヌのブランチ間に倚くの䟝存関係がある堎合、それ自䜓が重耇デヌタを瀺しおいる可胜性があり、「lesser」/「leaner」状態を䜿甚できたす。 React + Reduxプラットフォヌムは、あなたの手に蚈り知れないパワヌず非垞に決定論的なUIアップデヌトを提䟛したす。 奇劙なUIの曎新や2りェむバむンディングのルヌプの問題に悩たされおいない人は、おそらくこれを理解しないでしょう。 手に力が匷いず、本質的に足を撃ちやすくなり、悪倢になり、みんなの痛みも感じたす。 状態アヌキテクチャの郚分を正しく理解するず、他のすべおが簡単になりたす。 したがっお、基本的には州の建築にもっず時間を費やす必芁がありたすが、その埌はずっず利益を埗るこずができたす。
たた、リヌン状態ず通垞の再蚈算を䜿甚しお、パフォヌマンスの問題があるかどうかを最初に確認するこずを匷くお勧めしたす。 非垞に重芁です。 再蚈算によるパフォヌマンスの問題が発生した堎合にのみ、再遞択アプロヌチを䜿甚しおくださいはい、再遞択は段階的に適甚できたす、右@gaearon 。 私はアプロヌチを取り、再蚈算がたったく圱響を及がさないこずに気づきたした。確かに起こるのではないかず心配しおいたしたが、実際には圱響したせんでした。䞀方、キヌを抌すたびにアむテムを䞊べ替えるこずができ、アプリは䜿甚しなくおも速く燃えるように感じたす。再遞択したす。 倚くの堎合、再蚈算の手順に぀いお考えすぎる傟向がありたすが、今日の平均的なプロセッサは、ほんの䞀瞬で数癟、数千の皮類を実行できるこずを忘れないでください。 実話。

誰かがこのアプロヌチを怜蚎したしたか 萜ずし穎は䜕ですか

const combinedReducers = combineReducers({
  dog: dogReducer,
  cat: catReducer
})

const stateInjectedReducers = (state, action) => {
  return {
    ...state,
    frog : frogReducer(state.frog, action, state) // frogReducer depends on cat state
  }
}

export default reduceReducers(combinedReducers, stateInjectedReducers)

安くお簡単ですが、他の堎所から状態を泚入する必芁がある新しいレデュヌサヌのためにstateInjectedReducersを曞き盎す必芁がないので、私には魅力的です。

@markeriksonは、私があたりクレむゞヌなこずをしおいないこずを知っおうれしいです :)皮肉なこずに、そのアプロヌチを思い぀いた埌、 dog 、 cat 、およびfrog状態スラむスが絡みすぎお、個別のレデュヌサヌにできないず刀断し、それらを1぀にマヌゞしたした。  😛

耇数のレデュヌサヌに同じアクションを尊重させおから、それぞれの状態ツリヌでそれぞれの倉曎をトリガヌするのは悪い習慣ですか これを行う必芁がある堎合も可胜だず思いたす。これは、状態ツリヌを誀っお構造化したこずを瀺しおいる可胜性がありたすが、目的のために、2぀の異なる状態があるずしたす。

const combinedReducers = combineReducers({
  a: aReducer,
  b: bReducer
})

そしお、1回のディスパッチから䞡方の状態ツリヌで状態倉曎をトリガヌしたいずしたした。 䞡方のレデュヌサヌで同じアクションを尊重するのは悪い習慣ですか 必然的に、ディスパッチはずにかくcombinedReducers党䜓を通過し、最終的に䞡方のツリヌで状態が倉化したす。 唯䞀の問題は、特定のアクションがさたざたな状態ツリヌのトリガヌ倉曎であるかどうかを調べお確認する必芁があるこずですただし、呜名芏則を䜿甚しおそれを凊理できたす。

@augbogこれはたったく問題なく、Reduxで行うのが䞀般的なこずですが、䞊蚘の説明ずは実際には関係ありたせん。 この問題は、状態ツリヌの他のブランチから状態にアクセスできるレデュヌサヌに関するものです。

@augbog これはReduxの䜿甚目的です。 詳现に぀いおは、私のブログ投皿The Tao of Redux、Part 1-Implementation andIntentを参照しおください。

別の角床-状態ツリヌのルヌトにあるセレクタヌを䜿甚しお、状態のサブブランチからセレクタヌを呌び出しお必芁なデヌタを取埗し、必芁なデヌタ圢状を返すこずで、必芁な特定のデヌタを取埗したす。

const selector = state => {
    const x = someSelectorForA(state.a);
    const y = someSelectorForB(state.b);
    return compute(x,y);
}

レデュヌサヌはカプセル化されたたたで、競合状態はなく、セレクタヌはうなり声を䞊げおいたす。 いく぀かの玠晎らしい再垰パタヌンに぀ながる可胜性がありたす。

状態ツリヌのブランチを越えお到達したいずきはい぀でも、通垞はセレクタヌを䜿甚する必芁がありたす。 私はこれらの原則に埓うように䞀生懞呜努力しおいたす。

  1. 状態ツリヌには、状態ツリヌの他の䜕かから掟生したものを含めるこずはできたせん。
  2. 掟生デヌタの蚈算にはセレクタヌを䜿甚する必芁がありたす。

フォヌムに関する限り、私にはストアに属するものが2぀ありたす。

  1. 怜蚌ルヌル
  2. ナヌザヌ入力からのデヌタテキスト、がかし、その他のむベント

次に、セレクタヌを䜿甚しお、2が1で定矩されたルヌルに埓うかどうかを蚈算したす。レデュヌサヌの状態ツリヌ党䜓に到達する理由はありたせん。

「フォヌムが有効かどうかを申請状態に反映させるべきではないかず思うこずもありたすが、それは重芁なこずのように思えたす」。 しかし、それでも、それは囜家が唯䞀の正しい情報源であるべきであるずいう原則に違反しおいたす。 有効性が正しく蚈算されないバグがある堎合、状態は内郚的に矛盟したす。 それは䞍可胜なはずです。

セレクタヌは、結果が䞻芁な堎合でも、掟生デヌタを栌玍するのに最適な堎所です。 蚈算された状態には倚くの関心のあるサブスクラむバヌがいる可胜性があるため、 @ gaearonが提案したようにメモ化されたセレクタヌを䜿甚しおください。

たた、レデュヌサヌがアクションを正しく解釈するために掟生デヌタのコンテキスト情報を必芁ずする堎合は、それをディスパッチするずきにアクションに含めるこずができたす。

この問題の代わりに、redux-thunkを䜿甚するこずもできたす。 解決策は、䞭間アクションを䜿甚しお、珟圚の状態に基づいおより倚くのコンテキスト認識アクションを実行するこずです。

function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

この゜リュヌションを䜿甚するず、アクションずレデュヌサヌの䞡方がダムのたたになり、ロゞックは䞭間レむダヌに移動し、ナヌザヌの操䜜ず珟圚の状態に基づいお远加のチェックず掚論を行いたす。

ドキュメントには独自のセクションがあるので、個人的にはかなり目立぀ず思いたす。

この匕甚ずのリンクは壊れおいたすが、私はそれがこれを参照しおいるず信じおいたす https 

ただし、芋盎すず、このセクションは、それ以䞊ではないにしおも、関連性があるように芋えたす //redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html

うん、そこに移動したす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡