Redux 在全局组件中处理域特定的操作
Redux handling domain specific actions in a global component
为了模块化,我的 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
或 promise 中间件之类的东西很简单。
所以 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
子树上设置不同的状态属性。问题是,我还想在 app
子树中设置一个 loading: true
,这样我的高级 app
组件就会呈现一个漂亮的加载指示器。但是 usersReducer
没有得到状态树的 切片 。
无需编写将向所有 reducer 发送整个状态树的自定义 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
我创建的每个新操作都需要 ui 加载指示器。
2)
第二种方法是创建另外两个名为 startLoading
和 stopLoading
的操作,它们将从异步操作中调用,如下所示:
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 个动作而不是一个动作看起来太浪费了。
我想这是一个常见问题,但我找不到任何实用的 "real life" 建议。
我很乐意听到解决此问题的任何其他方法或对给定建议的想法。
Redux FAQ 中涵盖了该主题。目前,是的,答案是"write additional custom reducer logic",或"use getState
to put more data into actions"。
也就是说, 目前正在讨论一个 PR,它会让 combineReducers
将整个状态作为附加参数传递下去。参见 Feature Request: Allow reducers to consult global state。
此外,要回答 Pavel Tarno 的评论:是的,每个调度都会调用每个子减速器函数,如果 您正在使用 combineReducers
(请参阅 Redux FAQ);是的,每次调度都会调用所有订阅者;但是 React Redux connect()
函数只会强制重新渲染 return modified/updated 来自 mapStateToProps
的订阅组件。此外,由于 React 的批处理,即使事件循环中的多个连续分派每次都导致给定组件需要重新渲染,也可能只有一个 actual 重新渲染。 (最后是"virtual DOM",不是"shadow DOM"。)
为了模块化,我的 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
或 promise 中间件之类的东西很简单。
所以 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
子树上设置不同的状态属性。问题是,我还想在 app
子树中设置一个 loading: true
,这样我的高级 app
组件就会呈现一个漂亮的加载指示器。但是 usersReducer
没有得到状态树的 切片 。
无需编写将向所有 reducer 发送整个状态树的自定义 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
我创建的每个新操作都需要 ui 加载指示器。
2)
第二种方法是创建另外两个名为 startLoading
和 stopLoading
的操作,它们将从异步操作中调用,如下所示:
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 个动作而不是一个动作看起来太浪费了。
我想这是一个常见问题,但我找不到任何实用的 "real life" 建议。 我很乐意听到解决此问题的任何其他方法或对给定建议的想法。
Redux FAQ 中涵盖了该主题。目前,是的,答案是"write additional custom reducer logic",或"use getState
to put more data into actions"。
也就是说, 目前正在讨论一个 PR,它会让 combineReducers
将整个状态作为附加参数传递下去。参见 Feature Request: Allow reducers to consult global state。
此外,要回答 Pavel Tarno 的评论:是的,每个调度都会调用每个子减速器函数,如果 您正在使用 combineReducers
(请参阅 Redux FAQ);是的,每次调度都会调用所有订阅者;但是 React Redux connect()
函数只会强制重新渲染 return modified/updated 来自 mapStateToProps
的订阅组件。此外,由于 React 的批处理,即使事件循环中的多个连续分派每次都导致给定组件需要重新渲染,也可能只有一个 actual 重新渲染。 (最后是"virtual DOM",不是"shadow DOM"。)