【问题标题】:How to put methods onto the objects in Redux state?如何将方法放到 Redux 状态的对象上?
【发布时间】:2015-11-27 22:50:16
【问题描述】:

根据docs,react 应用程序的状态必须是可序列化的。 那么上课呢?

假设我有一个 ToDo 应用。 到目前为止,Todo 的每个项目都具有namedate 等属性。 现在我想对不可序列化的对象使用方法。 IE。 Todo.rename() 会重命名 todo 并做很多其他事情。

据我了解,我可以在某处声明函数并执行 rename(Todo) 或通过道具 this.props.rename(Todo) 将该函数传递给组件。

我在某处声明 .rename() 有两个问题: 1)在哪里?在减速机?在应用程序周围的减速器中的某处很难找到所有 would be instance 方法。 2)传递这个函数。真的吗?我应该通过所有更高级别的组件手动传递它吗 每次我有更多的方法时,都会添加大量的样板来传递它? 或者总是这样做并希望我对一种类型的对象只有一种重命名方法。不是Todo.rename()Task.rename()Event.rename()

这对我来说似乎很愚蠢。对象应该知道可以对它做什么以及以何种方式。不是吗?

我在这里缺少什么?

【问题讨论】:

  • 你应该看看Immutable Records 处理你想做的很好
  • 它没有说明任何方法,只是预定义的一组带有可选默认值的字段。
  • 您可以通过创建Records 对象的子类来添加方法,如文档中所述。但是,顺便说一下,你完全可以通过方法传递一个简单的对象,它们是可序列化的。

标签: javascript reactjs flux redux


【解决方案1】:

在 Redux 中,您并没有真正的自定义模型。您的状态应该是普通对象(或不可变记录)。他们不应该有任何自定义方法。

您应该编写处理动作的reducer,而不是将方法放在模型上(例如TodoItem.rename)。这就是 Redux 的全部意义所在。

// Manages single todo item
function todoItem(state, action) {
  switch (action.type) {
    case 'ADD':
      return { name: action.name, complete: false };

    case 'RENAME':
      return { ...state, name: action.name };

    case 'TOGGLE_COMPLETE':
      return { ...state, complete: !state.complete };

    default:
      return state;
  }
}

// Manages a list of todo items
function todoItems(state = [], action) {
  switch (action.type) {
    case 'ADD':
      return [...state, todoItem(undefined, action)];

    case 'REMOVE':
      return [
        ...state.slice(0, action.index),
        ...state.slice(action.index + 1)
      ];

    case 'RENAME':
    case 'TOGGLE_COMPLETE':
      return [
        ...state.slice(0, action.index),
        todoItem(state[action.index], action),
        ...state.slice(action.index + 1)
      ];
  }
}

如果这仍然没有意义,请通读Redux basics tutorial,因为您似乎对 Redux 应用程序的结构有一个错误的想法。

【讨论】:

  • 但是如果你想做一些诸如“获取所有可见的待办事项”之类的事情,你会在哪里放置过滤功能?将该功能添加到状态中是否合适?还是不赞成?
  • 这是不受欢迎的。相反,我们建议单独编写此函数以接受 state 参数。我们称这些函数为“选择器”,事实上它们对于 Redux 中的优化很重要。查看redux.js.org/docs/recipes/ComputingDerivedData.html
  • 在大多数面向对象的代码中,方法用于获取和设置数据。例如,一个 Todo 项目可能有 .getName()。其中一些可能有逻辑:getUrgency() 方法可能会查看任务的到期日期及其优先级,并进行一些数学运算以返回答案。如果不将这些函数定义为 store 中对象的方法,应该在哪里定义这些函数?
  • @DanAbramov 我很好奇 josh 的最后一个问题的答案。我正在处理类似的情况,其中我有一些属性的总和,但是在哪里放置它而不是 Class model .method?在效果模块中?在选择器中?什么是最好的?检查我更具体的问题:stackoverflow.com/questions/43489294/… 我试图阻止将new Model() 放到商店中......
  • 如果您需要对存储在您的状态中的对象使用有用的方法,例如定义一个名为“User”的模块,并在其中放入一些有用的功能,例如 User.fullName(user) .或“用户(用户)。全名()”。随心所欲,但存储中的数据应该是普通对象。
【解决方案2】:

就从头开始编写应用程序时应该如何使用 redux 而言,Dan 的回答当然是正确的。我的回答是基于可能类似于 OP 情况的非理想激励情况以及我正在考虑如何处理它。

我发现自己正在尝试创建一个 redux 应用程序,该应用程序依赖于 redux reducer 中的第 3 方库,这些库使用方法对输入对象执行操作,并且我希望将这些输入对象保持在 redux 状态。这是我在伪代码中的激励情况,以及我如何使用 lodash 的 assign 方法“解决”它:

// Library A is a large 3rd party library I don't want to change
// Library A expects instances of MyClass to have a method 'complexOperation', and also to have property 'a'
LibraryA = class {
  static reliesOnComplexOperation(myClassInstance) {
    if (myClassInstance.complexOperation() && myClassInstance.a < 100) {
      return true;
    } else {
      return false;
    }
  }
}

// MyClass is my own class that I control, and I want to store it's state and make changes to it using the redux reducer.
MyClass = class {
  constructor() {
    this.a = 0;
  }
  myComplexOperation() {
    return a > 10;
  }
}

//and here is my redux reducer, making use of Library A
function reducer(state, action) {
  switch (action.type) {
    case CHANGE_MY_CLASS:
      const myClassInstancePlain = state.myClassInstance;
      // redux has converted myClassInstance into a plain object with no methods, so we need to create a new instance of MyClass, and assign property values from the plain object to the new instance. Using the assign function from lodash library to achieve this - likely not the most efficient way
      const myClassInstance = _.assign(new MyClass(), myClassInstancePlain);

      // now I can pass myClassInstance off to LibraryA, and the complexOperation method will be available
      if (LibraryA.reliesOnComplexOperation(myClassInstance)) {
        myClassInstance.a = 50;
      }
      // I can return myClassInstance as part of the new state, and redux will convert it to a plain object
      return {
        ...state,
        myClassInstance
      };
    default:
      return state;
  }
}

所以这个例子展示了一种将对象与 redux 状态的方法合并为普通对象的方法,然后添加回方法,以便它们可以在 redux reducer 中使用。很想听听 Dan 或其他任何人对这种模式的看法,或者如何做得更好。我在几个地方看到过 Dan 在 redux 中用方法存储对象是一个坏主意,所以这里的目标是找到一种方法将对象存储为普通对象,并在需要时以有效的方式附加方法.

【讨论】:

  • Redux 根本不会将您的状态转换为痛苦对象。您的状态最终存储了一个类实例。它不会是痛苦的对象。
  • @DDS 据我所知,这是错误的。 (不过我懒得证明 atm 了:P)
  • @Venryx 除了我的错别字之外,我所说的完全正确,我也懒得去证明它。 Here's 设置减速器的代码直接从您传递给 createStorehere's 代码调用它并获取您返回的任何内容并将其存储为状态。无需查看或将其转换为普通对象。
  • 是的@Venryx 是对的,redux 不转换对象。在我的情况下,导致 redux 存储以普通对象结束的原因(可能是 DDS 也看到它的原因)是我正在执行服务器端渲染,然后序列化 redux 存储以将其从服务器端传递到客户端.序列化过程将存储中的所有内容转换为普通对象。
【解决方案3】:

我不确定这是否完全相关(上下文使用 Redux 和 React),但我在搜索类似内容时发现了这个页面,所以我写这个以防万一。

我的动机是,我有状态对象,我想在其中公开该状态的表示,而不是状态本身。例如(使用 Immutable.js),我有一个包含一个或多个 字段 的状态(schema),每个字段由唯一标识符标识并存储在 Map ,加上一个标识符列表,该列表给出了字段排序。

我真的不想写这样的东西

state.get('field').get(state.get('order').get(nth))

获取第n个字段,而不是靠近reducer的任何地方,以便隐藏内部表示并且可以轻松更改。

我目前正在做的(这是一个实验),是在与 reducer 相同的文件中添加一个函数:

schema.mapVisibleState = function (state) {
    return {
        getOrderedFields: () => state.get('order').map((fid) => state.get('fields').get(fid)
    }
}

这在相应的 React 组件中使用(以及类似的 schema.bindActionCreators 函数):

export const Schema = connect(
    (state) => schema.mapVisibleState (state),
    (dispatch) => schema.bindActionCreators (dispatch)
)(_Schema) ;

现在我可以像 this.props.getOrderedFields() 一样以正确的顺序访问组件内部的字段,而不必担心底层表示。它还有一个额外的好处,就是很容易对 getOrderedFields() 函数进行依赖注入。

另外,由于我有一个类似的字段状态结构,我可以扩展它:

schema.mapVisibleState = function (state) {
    return {
        getOrderedFields: () => state.get('order').map((fid) => field.mapVisibleState(state.get('fields').get(fid)),
        getNthField (nth) => field.mapVisibleState(state.get('fields').get(state.get('order').get(nth)))
    }
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-20
  • 1970-01-01
相关资源
最近更新 更多