如何将方法放在Redux状态的对象上?

时间:2015-09-02 12:20:07

标签: javascript reactjs flux redux

根据docs状态,反应应用程序必须是可序列化的。 那么课程怎么样?

假设我有一个ToDo应用程序。 Todo个项目中的每个项目都包含namedate等属性。 现在我想在不可序列化的对象上使用方法。即Todo.rename()它会重命名todo并做很多其他事情。

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

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

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

我在这里缺少什么?

3 个答案:

答案 0 :(得分:35)

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

不是将方法放在模型上(例如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应用程序的结构有错误的想法。

答案 1 :(得分:5)

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

我发现自己尝试创建一个redux应用程序,它依赖于redux reducer中的第三方库,它们使用方法对输入对象执行操作,并且我希望将这些输入对象保持在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中是一个坏主意,所以这里的目标是找到一种方法将对象存储为普通对象,并在需要时以有效的方式附加方法

答案 2 :(得分:4)

我不确定这是否完全相关(上下文使用Redux和React),但我在搜索类似的东西时找到了这个页面,所以我写这个是为了感兴趣。

我的动机是,我有状态对象,我希望公开该状态的表示而不是状态本身。因此,例如(使用Immutable.js),我有一个状态(架构),它包含一个或多个字段,每个字段由唯一标识符标识并存储在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)))
    }
}