设计/组合redux减速器

时间:2017-07-08 12:51:18

标签: reactjs redux react-redux

我正在清理我从一开始就错误的减速机设置。我使用redux docothe ducks pattern来设置正确。

我希望将我的状态分开/分开,如doco suggests

{
    domainData1 : {},
    domainData2 : {},
    appState1 : {},
    appState2 : {},
    ui : {
        uiState1 : {},
        uiState2 : {},
    }
}

在我的情况下,我有一些我想要管理的专辑,目录和照片。国家应该看起来像这样:

{
    //domainData
    albums : {...},
    catalogs : {...},
    photos : {...},

    //appState
    selectedAlbum : {...},

    //uiState
    ui : {
        loading : {},
        uiState2 : {},
    }
}

我已将属于相册的所有代码放在一个文件中,其中包含操作,操作创建者,重复者和API 类似的目录和照片。 然后我将reducers和combineReducers组合在一个单独的文件中。

以下代码......

我的问题是......当我在相册上做东西时,我会希望所有的域数据都进入州的专辑分支。但是我不希望ui和app状态的东西进入专辑分支 - 这应该进入应用程序和ui州分支机构。我的设置无法实现,例如加载专辑时我在“专辑”缩减器中设置加载标志,这是馈送到combineReducer,结果是加载状态成为状态专辑分支的一部分......

专辑模块:

import { createRequest, responseHandler, notAuthorized } from './apiUtils';
import { List, Map, fromJS } from 'immutable';

//actions
const REQUEST_ALBUMS = 'REQUEST_ALBUMS';
const FETCH_ALBUMS_SUCCESS = 'FETCH_ALBUMS_SUCCESS';
const FETCH_ALBUM_SUCCESS = 'FETCH_ALBUM_SUCCESS';
const REQUEST_ALBUM = 'REQUEST_ALBUM';
const CREATE_ALBUM = 'CREATE_ALBUM';
const CREATE_ALBUM_SUCCESS = 'CREATE_ALBUM_SUCCESS';
const UPDATE_ALBUM = 'UPDATE_ALBUM';
const UPDATE_ALBUM_SUCCESS = 'UPDATE_ALBUM_SUCCESS';

// Reducer
var init = Map(fromJS({
  albums: [],
  album: [],
  loading: false,
}));

var newState = null;

export function reducer(state=init, action={}) {
  switch (action.type) {

    case FETCH_ALBUMS_SUCCESS: {
      newState = state
        .set('albums', fromJS(action.payload.albums))
        .set('loading', false);
      return newState;
    }

    case REQUEST_ALBUMS: {
      return state.set('loading', true);
    }

    case FETCH_ALBUM_SUCCESS: {
      newState = state
        .set('album', fromJS(action.payload.album))
        .set('loading', false);
      return newState;
    }

    case REQUEST_ALBUM: {
      return state.set('loading', true);
    }

    case CREATE_ALBUM: {
      return state.set('loading', true);
    }

    case CREATE_ALBUM_SUCCESS: {
      newState = state
        .set('album', fromJS(action.payload.album))
        .set('loading', false);
      return newState;
    }

    case UPDATE_ALBUM: {
      return state.set('loading', true);
    }

    case UPDATE_ALBUM_SUCCESS: {
      newState = state
        .set('album', fromJS(action.payload.album))
        .set('loading', false);
      return newState;
    }
  }
  return state;
}

// Action Creators
export function getAlbumsPending(response) {
  return {
    type: REQUEST_ALBUMS,
  };
}

export function getAlbumsSuccess(response) {
  return {
    type: FETCH_ALBUMS_SUCCESS,
    payload: response,
  };
}

export function getAlbumPending(response) {
  return {
    type: REQUEST_ALBUM,
  };
}

export function getAlbumSuccess(response) {
  return {
    type: FETCH_ALBUM_SUCCESS,
    payload: response,
  };
}

function createAlbumPending(response) {
  return {
    type: CREATE_ALBUM,
  };
}

function createAlbumSuccess(response) {
  return {
    type: CREATE_ALBUM_SUCCESS,
    payload: response,
  };
}

export function updateAlbumPending(response) {
  return {
    type: UPDATE_ALBUM,
  };
}

export function updateAlbumSuccess(response) {
  return {
    type: UPDATE_ALBUM_SUCCESS,
    payload: response,
  };
}

//API

export function fetchAlbums() {
  //API url
  const url = '/api/albums.json';

  //make the call
  return dispatch => {

    dispatch(getAlbumsPending());

    fetch(createRequest('GET', url, null))
    .then(response => responseHandler(response))
    .then(data => dispatch(getAlbumsSuccess(data)))
    .catch(error => console.log('request failed', error));
  };
}

export function fetchAlbum(id) {
  //url
  var url = '/api/albums/'.concat(id);

  return dispatch => {
    dispatch(getAlbumPending());

    fetch(createRequest('GET', url, null))
    .then(response => responseHandler(response))
    .then(data => dispatch(getAlbumSuccess({ album: data })))
    .catch(error => console.log('request failed', error));
  };
}

export function createAlbum(params) {
  //API url
  const url = '/api/albums/';

  return dispatch => {

    dispatch(createAlbumPending());

    fetch(createRequest('POST', url, params))
    .then(response => responseHandler(response))
    .then(data => dispatch(createAlbumSuccess({ album: data })))
    .catch(error => console.log('request failed', error));
  };
}

export function updateAlbum(params) {
  //API url
  var url = '/api/albums/'.concat(params.id);

  //make the call
  return dispatch => {

    dispatch(updateAlbumPending());

    fetch(createRequest('PUT', url, params))
    .then(response => responseHandler(response))
    .then(data => dispatch(updateAlbumSuccess({ album: data })))
    .catch(error => console.log('request failed', error));
  };
}

CombineRecucer:

import { combineReducers } from 'redux';
import { reducer as albumReducer } from '../actions/album';
import { reducer as catalogReducer } from '../actions/catalog';

export default combineReducers({
  albums: albumReducer,  // <- loading flag ends up here
  catalogs: catalogReducer,
  loading: // I want loading flag here 
}); 

对不起,很长的帖子 - 没有土豆

1 个答案:

答案 0 :(得分:1)

您只需将该逻辑移动到负责该状态切片的Loading reducer。

// ducks/Loading.js

import {
  REQUEST_ALBUM,
  CREATE_ALBUM,
  CREATE_ALBUM_SUCCESS
} from 'ducks/Albums'  
// remember to also export from the Albums duck.

export const reducer = (state, action) => {
  switch (action.type) {
    case REQUEST_ALBUM:
    case CREATE_ALBUM:
      return state.set('loading', true)

    case CREATE_ALBUM_SUCCESS:
      return state.set('loading', false)
  }
}

您可以从其他duck文件导入操作类型。 Albums reducer只负责更新自己的状态切片。

多个Reducer可以侦听相同的动作类型,并对自己的切片进行一些更改。所以CREATE_ALBUM_SUCCESS也可以触发Albums reducer中的一些更改。

您也可以通过其他方式实现此目的。例如,通过在操作的meta字段中使用密钥。这避免了必须进行大量的导入和导出,因为reducer不必知道动作的类型。

// loading reducer
export const reducer = (state = false, action) => 
  (action.meta && action.meta.loading !== undefined)
    ? action.meta.loading
    : state


// action creator
export const updateAlbumPending = () => ({
  type: UPDATE_ALBUM,
  meta: {
    loading: true
  }
})