redux/react app中的状态具有属性为reducer的属性

Kar*_*ták 15 javascript state reactjs redux

我正在使用Redux和React创建一个应用程序.我遇到一个问题,我无法将状态映射到组件属性,因为状态具有与我使用的reducer的名称匹配的属性.

根减少器是使用combineReducers方法创建的

const rootReducer = combineReducers({
  appReducer
});
Run Code Online (Sandbox Code Playgroud)

最初的状态是

const initialState = {
  sources: [], 
  left: {}, 
  right: {},
  diff: {} 
}
Run Code Online (Sandbox Code Playgroud)

但是在组件功能中mapStateToProps:

function mapStateToProps(state) {
  return {
    sources: state.sources
  }
}
Run Code Online (Sandbox Code Playgroud)

state.sourcesundefined因为state参数的值是

{
  appReducer: {
    sources: [], 
    left: {}, 
    right: {}, 
    diff: {}
  }
}
Run Code Online (Sandbox Code Playgroud)

这是redux的一个特性吗?所以当我使用更多reducers时,所有这些都会为state变量添加新属性?或者我身边有什么问题(我在redux教程中从未注意到这种行为).

谢谢

Dan*_*mov 28

如果您只有一台减速机,则不需要combineReducers().直接使用它:

const initialState = {
  sources: [],
  left: {},
  right: {}
}
function app(state = initialState, action) {
  switch (action.type) {
  case 'ADD_SOURCE':
    return Object.assign({}, state, {
      sources: [...state.sources, action.newSource]
    })
  case 'ADD_SOURCE_TO_LEFT':
    return Object.assign({}, state, {
      left: Object.assign({}, state.left, {
        [action.sourceId]: true
      })
    })
  case 'ADD_SOURCE_TO_RIGHT':
    return Object.assign({}, state, {
      right: Object.assign({}, state.right, {
        [action.sourceId]: true
      })
    })
  default:
    return state
  }
}
Run Code Online (Sandbox Code Playgroud)

现在您可以使用该reducer创建一个商店:

import { createStore } from 'redux'
const store = createStore(app)
Run Code Online (Sandbox Code Playgroud)

并将组件连接到它:

const mapStateToProps = (state) => ({
  sources: state.sources
})
Run Code Online (Sandbox Code Playgroud)

但是你的reducer很难阅读,因为它会同时更新许多不同的东西.现在,是您想要将其拆分为多个独立Reducer的时刻:

function sources(state = [], action) {
  switch (action.type) {
  case 'ADD_SOURCE':
    return [...state.sources, action.newSource]
  default:
    return state
  }
}

function left(state = {}, action) {
  switch (action.type) {
  case 'ADD_SOURCE_TO_LEFT':
    return Object.assign({}, state, {
      [action.sourceId]: true
    })
  default:
    return state
  }    
}

function right(state = {}, action) {
  switch (action.type) {
  case 'ADD_SOURCE_TO_RIGHT':
    return Object.assign({}, state, {
      [action.sourceId]: true
    })
  default:
    return state
  }    
}

function app(state = {}, action) {
  return {
    sources: sources(state.sources, action),
    left: left(state.left, action),
    right: right(state.right, action),
  }
}
Run Code Online (Sandbox Code Playgroud)

这更易于维护和理解,并且还可以更轻松地独立更改和测试减速器.

最后,作为最后一步,我们可以使用combineReducers()生成根app减速器而不是手动编写它:

// function app(state = {}, action) {
//   return {
//     sources: sources(state.sources, action),
//     left: left(state.left, action),
//     right: right(state.right, action),
//   }
// }

import { combineReducers } from 'redux'
const app = combineReducers({
  sources,
  left,
  right
})
Run Code Online (Sandbox Code Playgroud)

使用combineReducers()而不是手动编写根减速器没有什么大的好处,除了它稍微更有效并且可能为您节省一些拼写错误.此外,您可以在应用程序中多次应用此模式:将不相关的Reducer以嵌套方式多次组合到单个reducer中可以.

所有这些重构都不会对组件产生影响.

我建议你在Redux上观看我的免费Egghead课程,课程涵盖了这种减速器组成模式,并展示了如何combineReducers()实现.


Jos*_*eau 5

实际上,我相信你的初始状态是:

{
  appReducer: {
    sources: [],
    left: {},
    right: {},
    diff: {}
  }
}
Run Code Online (Sandbox Code Playgroud)

这是因为combineReducers通过获取reducer的名称并将其内容映射到该名称来工作.

另外,只是一个注释,但如果你要使用超过1个减速器,你的减速器的名称应该更具体appReducer,并且(仅我的个人意见)他们不需要这个词reducer.典型的应用程序可能如下所示:

combineReducers({
  user: userReducer,
  messages: messagesReducer,
  notifications: notificationsReducer
});
Run Code Online (Sandbox Code Playgroud)

然后,可以访问您的州:

state.user.email
state.messages[0]
Run Code Online (Sandbox Code Playgroud)

  • 这个想法是reducer的每个分支都负责其自己的`initialState`。您的根reducer应该没有initialState,并且每个reducer应该只包含它的东西。因此,您的`appReducer`将是一个带有源,左,右等键的对象。 (2认同)