简体   繁体   English

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

[英]State in redux/react app has a property with the name of the reducer

I am creating an app using Redux and React. 我正在使用Redux和React创建一个应用程序。 I run into a problem where I cannot map state to component properties since the state has a property that matches the name of the reducer I used. 我遇到一个问题,我无法将状态映射到组件属性,因为状态具有与我使用的reducer的名称匹配的属性。

The root reducer is created with combineReducers method root reducer是使用combineReducers方法创建的

const rootReducer = combineReducers({
  appReducer
});

The initial state is 最初的状态是

const initialState = {
  sources: [], 
  left: {}, 
  right: {},
  diff: {} 
}

However in the component function mapStateToProps : 但是在组件函数mapStateToProps

function mapStateToProps(state) {
  return {
    sources: state.sources
  }
}

The state.sources is undefined because the value of state parameter is state.sourcesundefined因为state参数的值是

{
  appReducer: {
    sources: [], 
    left: {}, 
    right: {}, 
    diff: {}
  }
}

Is this a feature of redux? 这是redux的一个特性吗? So when I use more reducers, all of them will add new property to state variable? 所以当我使用更多reducers时,所有这些都会为state变量添加新属性? Or is there something wrong on my side (I never noticed this behavior in redux tutorials). 或者我身边有什么问题(我在redux教程中从未注意到这种行为)。

Thanks 谢谢

If you only have a single reducer, you don't need combineReducers() . 如果您只有一个reducer,则不需要combineReducers() Just use it directly: 直接使用它:

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
  }
}

Now you can create a store with that reducer: 现在您可以使用该reducer创建一个商店:

import { createStore } from 'redux'
const store = createStore(app)

And connect a component to it: 并将组件连接到它:

const mapStateToProps = (state) => ({
  sources: state.sources
})

However your reducer is hard to read because it update many different things at once. 但是你的reducer很难阅读,因为它会同时更新许多不同的东西。 Now, this is the moment you want to split it into several independent reducers: 现在, 是您想要将其拆分为多个独立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),
  }
}

This is easier to maintain and understand, and it also makes it easier to change and test reducers independently. 这更易于维护和理解,并且还可以更轻松地独立更改和测试减速器。

Finally, as the last step, we can use combineReducers() to generate the root app reducer instead of writing it by hand: 最后,作为最后一步,我们可以使用combineReducers()来生成root app reducer而不是手动编写它:

// 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
})

There is no large benefit to using combineReducers() instead of writing the root reducer by hand except that it's slightly more efficient and will likely save you a few typos. 使用combineReducers()而不是手动编写根减速器没有什么大的好处,除了它稍微更高效并且可能会省去一些拼写错误。 Also, you can apply this pattern more than once in your app: it's fine to combine unrelated reducers into a single reducer several times in a nested way. 此外,您可以在应用程序中多次应用此模式:将不相关的Reducer以嵌套方式多次组合到单个reducer中可以。

All this refactoring would have no effect on the components. 所有这些重构都不会对组件产生影响。

I would suggest you to watch my free Egghead course on Redux which covers this pattern of reducer composition and shows how combineReducers() is implemented. 我建议你在Redux上观看我的免费Egghead课程,课程涵盖了这种减速器组成模式,并展示了如何实现combineReducers()

Actually, I believe your initial state would be: 实际上,我相信你的初始状态是:

{
  appReducer: {
    sources: [],
    left: {},
    right: {},
    diff: {}
  }
}

This is because combineReducers works by taking the name of the reducer, and mapping its contents to that name. 这是因为combineReducers通过获取reducer的名称并将其内容映射到该名称来工作。

Also, just a note, but if you're going to use more than 1 reducer, the names of your reducers should be more specific than appReducer , and (just my personal opinion) they don't need the word reducer . 另外,只是一个注释,但如果你要使用超过1个减速器,你的减速器的名称应该比appReducer更具体,并且(仅我的个人意见)他们不需要reducer这个词。 A typical app might look like this: 典型的应用程序可能如下所示:

combineReducers({
  user: userReducer,
  messages: messagesReducer,
  notifications: notificationsReducer
});

Then, your state could be accessed like: 然后,可以访问您的州:

state.user.email
state.messages[0]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM