web-dev-qa-db-ja.com

Reduxレデューサーの状態を更新する正しい方法

私はreduxとes6構文の初心者です。私は公式のreduxチュートリアルを使用してアプリを作成しますが、これは です。

以下にJSスニペットがあります。私のポイント-投稿リデューサーでREQUEST_POST_BODYとRECEIVE_POST_BODYのケースを定義する。主な困難-ストア内の正しいオブジェクトを見つけて更新する。

私は例のコードを使用しようとします:

  return Object.assign({}, state, {
    [action.subreddit]: posts(state[action.subreddit], action)
  })

しかし、投稿の単純な配列を使用しました。 IDで正しい投稿を見つける必要はありません。

ここに私のコード:

  const initialState = {
    items: [{id:3, title: '1984', isFetching:false}, {id:6, title: 'Mouse', isFetching:false}]
  }

  // Reducer for posts store
  export default function posts(state = initialState, action) {
    switch (action.type) {
    case REQUEST_POST_BODY:
      // here I need to set post.isFetching => true
    case RECEIVE_POST_BODY:
      // here I need to set post.isFetching => false and post.body => action.body
    default:
      return state;
    }
  }

  function requestPostBody(id) {
    return {
      type: REQUEST_POST_BODY,
      id
    };
  }

  function receivePostBody(id, body_from_server) {
    return {
      type: RECEIVE_POST_BODY,
      id,
      body: body_from_server
    };
  }

  dispatch(requestPostBody(3));
  dispatch(receivePostBody(3, {id:3, body: 'blablabla'}));
19
Max Vorozhcov

配列あり

配列に固執したい場合は、単一のpostオブジェクトに取り組むリデューサーを作成できます。

export default function reducePost(post, action) {
  if(post.id !== action.id) return post;

  switch(action.type) {
  case REQUEST_POST_BODY:
    return Object.assign({}, post, { isFetching: true });
  case RECEIVE_POST_BODY:
    return Object.assign({}, post, { isFetching: false, body: action.body });
  default:
    return post;
}

ルートレデューサーは次のようになります。

export default function posts(state = initialState, action) {
  return state.map(post => reducePost(post, action);
}

リスト内の各投稿に対して新しいレデューサーを実行するだけで、更新された投稿の配列を返します。この場合、一意のIDにより、必ず1つのアイテムのみが変更されます。

オブジェクト付き

各アイテムに一意の文字列/番号IDがある場合、配列を反転して、代わりにobjectを使用できます。

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  };
}

その後、減速機を簡素化できます。

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], { isFetching: true })
  });
case RECEIVE_POST_BODY:
  let id = action.id;
  return Object.assign({}, state, {
    [id]: Object.assign({}, state[id], {
      isFetching: false,
      body: action.body
    })
  });
default:
  return state;
}

ES7構文を試して満足している場合は、Babelでオブジェクトスプレッド演算子を有効にし、Object.assignの呼び出しを書き換えることができます。

switch (action.type) {
case REQUEST_POST_BODY:
  let id = action.id;
  return {
    ...state,
    [id]: {...state[id], isFetching: true }
  };
case RECEIVE_POST_BODY:
  let id = action.id;
  return {
    ...state,
    [id]: {
      ...state[id],
      isFetching: false,
      body: action.body
    }
  };
default:
  return state;
}

Spread構文の使用にあまり熱心でない場合は、Object.assignをもう少し口当たりの良いものにすることも可能です。

function $set(...objects) {
  return Object.assign({}, ...objects); 
}
case RECEIVE_POST_BODY:
  let id = action.id;
  return $set(state, {
    [id]: $set(state[id], {
      isFetching: false,
      body: action.body
    })
  });
28
Dan Prince

私が正しく理解していれば、あなたが望む特定の投稿を得るのに問題があります。

まず、リデューサーを使用すると、配列とその中のオブジェクトも更新されるため、読み取りと保守が難しくなります。アレイを使用したレデューサーの構成について説明するこの ショートビデオ をご覧になることをお勧めします。そこで説明されている手法を使用して、コードを単純化できます。

あなたの場合、posts減速機とpost減速機を使用し、posts減速機はpost減速機を呼び出します。

作業に適したオブジェクトを見つけることに関して、ダンプリンスの提案はそれを容易にします。配列の代わりにオブジェクトマップを使用すると、簡単になります。ダンの答えからの関連するコードスニペット:

const initialState = {
  items: {
    3: {id:3, title: '1984', isFetching:false},
    6: {id:6, title: 'Mouse', isFetching:false}
  ];
}
1
Moti Azu

動作するObject.assignを使用してオブジェクトレデューサーを実装しましたが、プロジェクトが成長し、多数の依存コンポーネントを追加すると、非常に効率が悪くなり、レンダリングが非常に遅くなります。

immer について知っていれば、最初からそれを使用していました。

基本的に、次のようにimmerを使用します。この例には、次のようなlayersオブジェクトがあります。

const initialState = {
  layers: {
   'layer1': { selected: false },
   'layer2': { selected: true }
  }
}

Reducers.js(抽出)

import produce from 'immer'
import has from 'lodash/has'
export const layers = (state = null, action) => {
  switch (action.type) {
    case ADD_LAYER:
      // Using `immer.produce` only for consistency 
      // clearly the whole object changes in this case.
      return produce(state, layers => {
        // Take a copy of the prebuilt layer
        var layer = Object.assign({}, action.layer)
        // Add some defaults
        if (!has(layer, 'selected')) {
          layer.selected = false
        }
        layers[action.id] = layer
      })
    case SELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = true
      })
    case UNSELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = false
      })
    default:
      return state
  }
}

Actions.js(抽出)

export const addLayer = id => ({
  type: ADD_LAYER,
  id
})

export const selectLayer = id => ({
  type: SELECT_LAYER,
  id
})

export const unSelectLayer = id => ({
  type: UNSELECT_LAYER,
  id
})

参照:

https://github.com/immerjs/immer

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

0
Simon Hutchison