【问题标题】:Canonical approach for implementing `combineReducers` in existing application在现有应用程序中实现“combineReducers”的规范方法
【发布时间】:2018-02-09 21:29:40
【问题描述】:

我有一个 redux 存储,到目前为止它非常简单,只需要一个 reducers 文件。但是我现在需要把它分开。使用combineReducers{reducerOne, reducerTwo} 是我必须重构我的应用程序以进入以下实例的方式:

const mapStateToProps = state => ({
  prop1: state.prop1,
  /*...many more...*/
})

并将其更改为:

const mapStateToProps = state => ({
  prop1: state.reducerOne.prop1,
  /*...many more...*/
})

还是有更规范的方法?如果我摆脱隐式返回并制作第一行 state = state.reducerOne 或其他内容,它可能会缩短一点。

有没有更好/更规范的方式来合并新命名的reducer?

【问题讨论】:

  • 我不确定是否有一个好的解决方案可以做到这一点。如果您像这样将这些项目添加到状态中,则会对状态容器所维护的状态产生负面影响。您可以将值复制到对象的该级别,但随后会被复制。如果您删除了原始值,它将无法按照 redux 原生的方式工作。
  • @JosephFehrman 我没有关注。将哪些项目添加到状态中?
  • 可以将 state.reducerOne.prop1 添加到根状态组件 state.prop1 中。您可以使用扩展运算符对 reducerOne 的所有属性执行此操作。我不建议这样做,因为它会在每次状态迭代时都会发生突变,而且我敢打赌 Redux 会很难处理它。
  • @JosephFehrman k 谢谢

标签: javascript reactjs redux react-redux reducers


【解决方案1】:

看看这些东西:


使用这些工具和方法,您最终会 使用从减速器导出的通用选择器, (它们正在从那些 reducer 负责的子状态中解析通用数据) 然后导入到 rootReducer 它们用于创建另一组通用选择器,但从状态的根解析相同的数据。

在此之后,您基本上可以采用两种概念上不同的方式:

  • 保留组件(我们称之为“容器”或“智能”组件)特定的选择器 连同容器组件本身(下面的示例更多地是关于这种特殊情况)

  • 有一个单独的“数据层”,它不太了解它将为其提供数据的组件 并保留面向数据/特征的选择器,使用您为项目定义的模式进行组织。

当然,您可以在某种程度上将两者结合起来。 但重要的是要弄清楚界限并将所有事物保留在它们所属的地方。


<...>/FooContainer/selectors.js 和你一起 <...>/FooContainer/FooContainer.jsx 组件。


reducers/entities.js

import { INITIAL_STATE } from 'initialState';

export default (state = INITIAL_STATE.entities, action) => {
  /* reducer */
};

export const getModelData = (entities, model, keyWindow) => {
  /*
   get model data from normalized entities store
   using model fields and the keyWindow
  */
};

reducers/index.js

// <...>

/* entities, location, contents - are reducers  */
import entities, * as fromEntities from './entities';
import location, * as fromLocation from './location';
import contents, * as fromContents from './contents';

// <...>

export const rootReducer = combineReducers({
  entities,
  location,
  contents,
});


const getEntities = state => state.entities;
const getLocation = state => state.location;
const getContents = state => state.contents;

// <...>

// this is a generic selector for getting the data from
// the entities store.
export const getModelData = createSelector(
  [getEntities, (state, model, keyWindow) => ({ model, keyWindow })],
  fromEntities.getModelData,
);

// ...

FooContainer/selectors.js

import { getModelData } from 'app/reducers';

export const componentData = createSelector(
  [getModelData, (state, model, keyWindow, props) => props],
  (modelData, props) => { /* do something specific for your component */},
);

FooContainer/FooContainer.jsx:

import { componentData } from './selectors';
import { FooModel } from 'fooFeature/models';
import { someFooAction, loadFooData } from 'fooFeature/actions';

const getKeyWindow = props => {/* return keyWindow */ };
const mapStateToProps = (state, props) => ({
  componentData: componentData(state, FooModel, getKeyWindow(props), props),
});
const mapDispatchToProps = {
  someFooAction,
  loadFooData,
};

@connect(mapStateToProps, mapDispatchToProps)
class FooContainer extends Component {
  static propTypes = {
    componentData: PropTypes.arrayOf(PropTypes.object),
    loadFooData: PropTypes.func.isRequired,
    someFooAction: PropTypes.func.isRequired,
  }

  /* <...> */
}

【讨论】:

  • 感谢分享,不确定是否适合 OP 的情况,但它是一种可扩展的方式来构建 redux 应用程序。
  • @Xlee,是的,也许这甚至不是特定问题的直接答案,但我认为从一开始就学会如何正确地做事总是好的。
【解决方案2】:

也许可以解构它:

const mapStateToProps = ({reducerOne}) => ({
  prop1: reducerOne.prop1,
  /*...many more...*/
})

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-13
    • 1970-01-01
    • 2013-12-10
    • 1970-01-01
    • 2017-01-19
    • 1970-01-01
    • 2018-04-13
    • 1970-01-01
    相关资源
    最近更新 更多