【问题标题】:Where to store reducers affecting global state在哪里存储影响全局状态的 reducer
【发布时间】:2017-07-04 15:36:35
【问题描述】:

这是我的店铺形状:

export default {
    isRequesting: false,
    requestError: null,
    things: [],
    otherThings: []
}

当从服务器获取 thingsotherThings 时,isRequesting 会更改,requestError 可能会更改。目前,我正在像 reducers/thingReducer.jsreducers/otherThingReducer.js 这样的减速器中更改这些,例如:

// reducers/thingReducer.js
import { combineReducers } from 'redux'
import { LOAD_THINGS_REQUESTING, LOAD_THINGS_SUCCESS, LOAD_THINGS_ERROR } from '../actions/actionTypes'
import initialState from './initialState'


export function things(state = initialState.things, action) {
    switch(action.type) {
        case LOAD_THINGS_SUCCESS: 
            return action.things
        default:
            return state
    }
}

export function isRequesting(state = initialState.isRequesting, action) {
    switch(action.type) {
        case LOAD_THINGS_REQUESTING:
            return true
        case LOAD_THINGS_SUCCESS:
            return false
        case LOAD_THINGS_ERROR:
            return false
        default:
            return state
    }
}

export function requestError(state = initialState.requestError, action) {
    switch(action.type) {
        case LOAD_THINGS_ERROR:
            return action.error
        default:
            return state
    }
}

const thingsReducer =  {
    things,
    isRequesting,
    requestError
}

export default thingsReducer

如您所见,我的 thingReducer 中有 isRequestingrequestError 的减速器,otherThingReducer 中也有同样的东西。

您可能还会看到我正在导出每个函数,以便我可以在rootReducer.js 中执行以下操作

const rootReducer = combineReducers({
    ...thingReducer,
    ...otherThingReducer
})

export default rootReducer

我从未在示例代码(展开reducer)中看到这样做,这让我认为每个reducer 文件应该只包含一个函数。我知道这是两个问题:

  1. isRequestingrequestError 是否应该存在于单独的 reducer 文件中(即使它们是全局状态的一部分)

  2. 如果是这样,它们是否应该像我上面所做的那样分散和组合。即使1)的答案是否定的,当每个reducer文件实际上需要多个reducer时,我可以使用这种spread/combine方法吗?

【问题讨论】:

    标签: javascript reactjs ecmascript-6 redux


    【解决方案1】:

    你没有分散减速器。 thingsReducer 是一个包含减速器的对象,您正在分散该对象。如果您使用 combineReducers,您可以将 thingsReducer 设为嵌套减速器,但我认为没有必要:

    const thingsReducer = combineReducers({
        things,
        isRequesting,
        requestError
    })
    

    至于您的问题,isRequestingrequestError 的位置并不重要。将每个减速器放在自己的模块中是一种常见的做法(检查 Ducks,例如 https://github.com/erikras/ducks-modular-redux),但这取决于您。您也可以完全删除 thingsReducer 并将减速器直接导入您的 rootReducer.js,如下所示:

    import {things, isRequesting, requestError} from './reducers/thingReducer'
    // Or if you decided to put each reducer in its own file
    // import things from './reducers/thingsReducer' 
    // import isRequesting from './reducers/isRequestingReducer' 
    // ...
    
    const rootReducer = combineReducers({
        things,
        isRequesting,
        requestError,
        // do the same for otherThingReducer reducers
    })
    
    export default rootReducer
    

    更新: 要在 cmets 中回答您的问题,您可以执行以下操作来简化您的代码。由于您正在重用许多操作,您可以像这样将您的减速器合并为一个:

    export function things(state = initialState, action) {
        switch(action.type) {
            case LOAD_THINGS_SUCCESS: 
                return {
                    ...state,
                    things: action.things,
                    isRequesting: false
                }
            case LOAD_THINGS_ERROR: 
                return {
                    ...state,
                    requestError: action.error,
                    isRequesting: false
                }
            case LOAD_THINGS_REQUESTING: 
                return {
                    ...state,
                    isRequesting: true
                }
    
            default:
                return state
        }
    }
    

    如果这样做,只需将默认的things减速器导入rootReducer即可。

    【讨论】:

    • 谢谢,我会将它们放在不同的文件中以保持干燥。然后,我想没有必要再传播了——谢谢!
    • 实际上,因为我有 LOAD_THINGS_SUCCESS 等。将它们与 thing 减速器捆绑在一起不是更有意义吗?是否有为任何和所有 AJAX 调用设置独立 LOAD_SUCCESS 的常见做法?如果是这样,发送LOAD_SUCCESSLOAD_PARTS_SUCCESS会不会很乱?
    • 您绝对可以重用来自其他模块的操作,或者拥有一组共享的通用化简器。另一种做法(再次来自 Ducks)是在动作名称前加上模块/reducer 名称。例如thing/LOAD_THINGS_REQUESTINGotherThing/LOAD_THINGS_REQUESTING。我不太喜欢重用动作,而是使用前缀方法来帮助我调试和跟踪我的应用程序的流程。
    • 好的,太好了 - 谢谢。那么,调度LOAD_REQUESTINGLOAD_ERROR 来影响全局状态,然后调度LOAD_THINGS_SUCCESS 来影响things 状态可以吗?
    • 谢谢,所以我将其更改为您的最新更新,但由于某种原因,我的商店现在是嵌套的 things -> things: [...] - 我认为这种嵌套发生在 LOAD_THINGS_SUCCESS 上,但不是被正确合并
    【解决方案2】:

    在你的 reducer 文件的底部,比如你的 things reducer,你可以像这样导出一个常量:

    export const thing = combineReducers({ things, isRequesting, requestError });
    

    然后在你的根减速器中,你以同样的方式组合它们:

    const rootReducer = combineReducers({
        thingReducer,
        otherThingReducer
    })
    
    export default rootReducer
    

    这是一个见仁见智的问题,但我更喜欢创建一个适合您的 thingsisRequestingrequestError 的减速器,如下所示:

    // reducers/thingReducer.js
    import { combineReducers } from 'redux'
    import { LOAD_THINGS_REQUESTING, LOAD_THINGS_SUCCESS, LOAD_THINGS_ERROR } from '../actions/actionTypes'
    import initialState from './initialState'
    
    const initialThing = {
      data: {},
      isLoading: false,
      isError: false
    }
    
    const things = (
      state = initialThing,
      action
    ) => {
        switch(action.type) {
          case LOAD_THING_REQUESTING:
            return {
              ...state,
              isLoading: true
            };
          case LOAD_THINGS_SUCCESS: 
            return {
              ...state,
              data: action.things,
              isLoading: false,
              isError: false
            };
          case LOAD_THINGS_ERROR:
            return {
              ...state,
              isLoading: false,
              isError: true
            };
          default:
            return state;
        }
    }
    
    export default thingsReducer = combineReducers({ things, someOtherReducerFunction });
    

    【讨论】:

    • 有趣,谢谢你的建议,我最初尝试做...state 的事情,但认为它会被认为是一种反模式——虽然看不出有什么不好的原因。您的应用相对复杂吗?
    • 这不是一个反模式,并且可能是从 reducer 返回一个新对象的最简洁的方式(记住,我们不想改变状态,总是返回一个新对象!)。您也可以通过使用 lodash 中的 assign 实用程序函数来完成此操作,例如 _.assign({}, state, {data: newDataHere}) 或普通的旧 Object.assign 即 Object.assign({}, state, {data: newDataHere})
    • 这里是 redux 中展开运算符的一个很好的资源,直接来自 redux 文档 :D redux.js.org/docs/recipes/UsingObjectSpreadOperator.html
    猜你喜欢
    • 2022-08-15
    • 1970-01-01
    • 2023-04-08
    • 2016-06-19
    • 2021-10-18
    • 2021-09-27
    • 2018-12-08
    • 2012-04-07
    • 2011-10-26
    相关资源
    最近更新 更多