web-dev-qa-db-ja.com

ビジネスロジックをどこに配置すればよいでしょうか。アクションまたはストア

私はRefluxからReduxに来ています。 Refluxでは、ビジネスロジックはストアにのみ存在しますが、-Reduxには異なるように見えます。たとえば、「Redux "の場合" async-action "そして "redux-thunk"で実装しました。

1つのシナリオでは、自分のアクションで何かをチェックし、必要に応じてサーバーにリクエストを送信してデータを取得します。この場合、アクションのロジックを確認する必要があります。実際、ビジネスロジックは実際に存在し、一緒に保存されています。これは問題です。解決策は何ですか。

たとえば、チェックボックスがあり、いくつかの条件をチェックし、結果がtrueの場合、ここにサーバーにリクエストを送信します。これは私のアクションコードであり、私のビジネスロジックは私のアクションとリデューサーにあります。

export function onCheckboxClick({itemId}) {
  return (dispatch, getState) => {
      let state = getState().get('myReducer');

      let myConditionResult = state.get('foods').get(0).get('test');//for exmaple check some condition in my store

      dispatch({type: 'CHECKBOX_CLICK', itemId});// for change the checkbox checked

      if (myConditionResult) {
        myApi.deleteOrderItem({itemId}).then(()=> {
          dispatch({type: 'DELETE_ORDER_ITEM_FULFILLED', itemId});
        }).catch((err)=> {
          console.log(err);
          dispatch({type: 'DELETE_ORDER_ITEM_REJECTED', itemId});
        });
      }
   };
}

前もって感謝します

15

前述のように、ユースケースに応じて、このアクションを実行する複数の方法があります。私ができることは、あなたのユースケースを推測することから、より適切であると思われることをリストアップすることです。

1。コンポーネント内のロジック

状態を保持する状態は、connect from react-redux を使用して状態をプロップにマッピングすることにより、コンポーネントに取り込むことができます。

また、アクションをこのコンポーネントファイルにインポートし、アクションをプロップにもマッピングします。

以下の例は、状態とアクションをコンポーネントファイルに取り込む方法を示しています。使い方はあなた次第です。私はそれを単純な文脈に入れました。したがって、ロジックを実行したい時点でmyFunction()を呼び出すことができます。

MyComponenet.js

import React, { Component} from 'react'
import { connect } from 'react-redux'
import { onCheckboxClick } from 'path/to/action'

class MyComponenet extends Component {

    myFunction() {
         const { theConditiion, onCheckboxClick } = this.props

         if (theConditiion) {
             onCheckboxClick({itemId: 'someItemid'})
         }
    }

    render() {
      //...
    }
 }


const mapStateToProps = (state) => ({
    theConditiion: state.wherever.the.data.lives.in.store
})

export default connect(
    mapStateToProps,
    { onCheckboxClick }
    )(MyComponenet)

したがって、上の例のonCheckboxClick関数内に現在ある条件チェックを削除できます。

2。ミドルウェア内にロジックを配置する

以下の例は、アクションをディスパッチする方法を示していますが、最初に特定のタイプのアクションを「キャッチ」し、条件がtrueであれば、API呼び出しを実行して、さらにアクションをディスパッチできます。falseの場合は、アクションを渡すだけです。次のミドルウェアへ。

myMiddleware.js

const onCheckboxClick = store => next => action => {
    if (action.type == 'CHECKBOX_CLICK') {

    let theConditiion = store.getState().wherever.the.data.lives.in.store

    if (theConditiion) {
        // 1. make the api call here, or,
        // 2. dispatch an action specific to handling api calls.
        // E.g. Create another middleware to catch action type `API_CALL` 
        // This middleware can then handle all api calls, and dispatch actions for api requests, responses and errors. 

        const newAction = {...action, type: 'API_CALL' }
        store.dispatch(newAction)

        // When you use store.dispatch(), the action will be passed back to the top of the middleware chain. 
    }

    return next(action) // this will pass the action to the next middleware in the chain.

}

export default onCheckboxClick

これは、最も効果的なものを考えるのに役立つ幅広い概要です。アプリの開発中に、繰り返しロジックを独自の関数にすることができることに気付くでしょう。

8
meteorBuzz

Redux FAQ「アクションクリエーターとレデューサーの間でビジネスロジックを分割する方法」 のエントリーを引用します(---):

リデューサーまたはアクションクリエーターでどのロジックを実行する必要があるかについての明確な答えは1つではありません。

すべてのロジックをアクションクリエーターに配置すると、状態の更新を宣言するファットアクションオブジェクトが作成されます。レデューサーは純粋で、無意味で、追加され、削除され、これらの関数が更新されます。簡単に作成できます。しかし、ビジネスロジックの多くは存在しません。レデューサーにロジックをさらに追加すると、データロジックのほとんどが1か所にある、ニースのシンアクションオブジェクトになりますが、他のブランチからの情報が必要になる可能性があるため、レデューサーの構成は難しくなります。あなたは大きなレデューサー、または州の上位から追加の引数を取るレデューサーで終わることになります。

レデューサーによって無視されるアクションをディスパッチすることは有効です。また、最初に状態を検査してアクションをディスパッチすることをしないことも有効です。最終的には、それはあなたが快適に感じるものに帰着します。

15
markerikson

これは、reduxの推奨事項に反する意見のある回答です。

TL; DR どちらでもない

より長い答え:いわゆる非同期アクションミドルウェアから呼び出されます。 reduxコミュニティでは、「サンク」または「サーガ」として知られています。

まず、いくつかの定義:

  • action:プレーンオブジェクト{ type: 'ACTION_TYPE', payload: { data } }
  • アクション作成者アクションを返す関数。
  • 非同期アクションミドルウェアから呼び出される関数。
  • 非同期アクション作成者非同期アクションを返す関数
  • ミドルウェア:すべてのアクションを処理し、他のアクションをディスパッチし、状態を保存するためのアクセス権を持つ関数。

では、どこからビジネスロジックを呼び出すのでしょうか。

注意深く見ると、async actionasync action creatorは必要ないことがわかります。 ミドルウェアで直接処理される単純なactionを使用できます。

ミドルウェアでは、各actionに専用のhandlerを使用できます。このhandlerasync actionのように動作しますが、そのように呼びません。それをinteractorと呼びましょう。

だから新しい定義:

interactor:reduxでは本質的にasync actionの抽象化ですが、redux固有ではありません。 Interactorはデータをフェッチし、ビジネスロジックを呼び出し、結果の「アクション」をディスパッチします。

middleware = (...) => {
  // if(action.type == 'HIGH_LEVEL') 
  interactors[action.name]({ dispatch, params: action.payload })
}

const interactors = {
  async highLevelAction({ dispatch, params }) {
    dispatch({ loading: true });
    const data = await api.getData(params.someId);
    const processed = myPureLogic(data);
    dispatch({ loading: false, data: processed });
  }
}

発送方法:

dispatch({ type: 'HIGH_LEVEL', name: 'highLevelAction', { someId: 1 } })
8
Vanuan