3

为了模块化,我的 Redux 应用程序被分解为单独的组件,app域是一种共享基础架构组件,它处理常见的 ui 更改,例如加载指示器。

app/ <- Global app  
---app_reducer.js  
---constants.js  
---actions.js
---...(react components)
users/
---users_reducer.js
---constansts.js
---actions.js
---...(react components)
products/
---products_reducer.js
---constants.js
---actions.js
---...(react components)

在我的根减速器中,我使用该combineReducers函数根据我的域很好地拆分状态树:

import { combineReducers } from 'redux';
import App from './app/app_reducer'
import Users from './users/users_reducers'
import Products from './products/products_reducers'

export default combineReducers({
  App,
  Users,
  Products
})

所以我的状态树是这样的:

{
   app: 
     isLoading: false,
   users:
     ['user1', 'user2', 'user3']
   products:
     ['product1', 'product2'],
     lastFetch: 1246578942621
}

好的,所以现在我想添加一些异步功能到users它们products从服务器获取数据的位置。redux-thunk使用类似或承诺中间件的东西是微不足道的。所以动作创建者users/actions.js可能看起来像这样:

import { LOAD_USERS_REQUEST, LOAD_USERS_SUCCESS } from './actions'

// Using redux-thunk
export function LoadUsers(){
  return function(dispatch){
     dispatch({type: LOAD_USERS_REQUEST})
     fetch('myserver.com/users')
     .then(function(data){
        dispatch({type: LOAD_USERS_SUCCESS, data })
     })
  }
}

请记住,它products也可能会做类似的事情来获取自身。
现在,处理这些动作并在子树上usersReducer设置不同的状态道具。users问题是,我还想loading: trueapp子树中设置一个,以便我的高级app组件能够呈现一个很好的加载指示器。但是usersReducer没有得到状态树的那一部分

如果不编写combineReducers将所有减速器发送到整个状态树的自定义,那么优雅的实现方式是什么?我正在寻找一个干净且可扩展的解决方案。这是我考虑的几种方法:

1)appReducer对每个新请求做出响应。这意味着每次为 fetch 创建新的操作类型时,我都需要将其添加到app/app_reducer.js

import * as productTypes from '../products/constants'
import * as userTypes from '../users/constants'

export default function(state = {}, action){
  switch(action.type){
    case productTypes.LOAD_USERS_REQUEST:
    case productTypes.LOAD_PRODUCTS_REQUEST:
      return({...state, isLoading: true});
    case productTypes.LOAD_USERS_SUCCESS:
    case productTypes.LOAD_PRODUCTS_SUCCESS:
      return({...state, isLoading: false})
    default:
      return(state);
  }
}

这样做的问题是我必须修改appReducer我创建的每个需要加载指示器的新操作。

2)第二种方法是创建另外两个被调用的动作startLoadingstopLoading它们将从异步动作中调用,如下所示:

import { LOAD_USERS_REQUEST, LOAD_USERS_SUCCESS } from './actions'
import { START_LOADING, STOP_LOADING } from '../app/actions'    

export function LoadUsers(){
  return function(dispatch){
     dispatch({type: START_LOADING})
     dispatch({type: LOAD_USERS_REQUEST})
     fetch('myserver.com/users')
     .then(function(data){
        dispatch({type: STOP_LOADING})
        dispatch({type: LOAD_USERS_SUCCESS, data })
     })
  }
}

乍一看,这似乎是个好主意,但调用 2 个动作而不是一个动作看起来很浪费。

我想这是一个常见问题,但我找不到任何实用和“现实生活”的建议。我很高兴听到任何其他解决此问题的方法或对给定建议的想法。

4

1 回答 1

2

Redux 常见问题解答中涵盖了该主题。目前,是的,答案是“编写额外的自定义 reducer 逻辑”,或“用于getState将更多数据放入操作中”。

也就是说,目前正在讨论一个 PR,它将combineReducers把整个状态作为一个额外的论点传递下去。请参阅功能请求:允许减速器咨询全局状态

另外,回答 Pavel Tarno 的评论:是的,如果您正在使用,每个调度都会调用每个子减速器函数combineReducers(请参阅Redux 常见问题解答);是的,每个分派都会调用所有订阅者;但是 React Reduxconnect()函数只会强制重新渲染订阅的组件,这些组件从mapStateToProps. 此外,由于 React 的批处理,即使在一个事件周期中多次连续分派每次都导致给定组件需要重新渲染,也可能只有一次实际的重新渲染。(最后,它是“虚拟 DOM”,而不是“影子 DOM”。)

于 2016-06-01T16:12:09.637 回答