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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32352982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:56:48  来源:igfitidea点击:

How to put methods onto the objects in Redux state?

javascriptreactjsfluxredux

提问by Yan

According to docsstate of react app has to be something serializable. What about classes then?

根据文档,react app 的状态必须是可序列化的。那上课呢?

Let's say I have a ToDo app. Each of Todoitems has properties like name, dateetc. so far so good. Now I want to have methods on objects which are non serializable. I.e. Todo.rename()which would rename todo and do a lot of other stuff.

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

As far as I understand I can have function declared somewhere and do rename(Todo)or perhaps pass that function via props this.props.rename(Todo)to the component.

据我所知,我可以在某处声明rename(Todo)函数,然后通过 props 将该函数传递this.props.rename(Todo)给组件。

I have 2 problems with declaring .rename()somewhere: 1) Where? In reducer? It would be hard to find all would be instancemethods somewhere in the reducers around the app. 2) Passing this function around. Really? should I manually pass it from all the higher level components via And each time I have more methods add a ton of boilerplate to just pass it down? Or always do and hope that I only ever have one rename method for one type of object. Not Todo.rename()Task.rename()and Event.rename()

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

That seems silly to me. Object should know what can be done to it and in which way. Is not it?

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

What I'm missing here?

我在这里缺少什么?

采纳答案by Dan Abramov

In Redux, you don't really have custom models. Your state should be plain objects (or Immutable records). They are not expected to have any custom methods.

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

Instead of putting methods onto the models (e.g. TodoItem.rename) you are expected to write reducers that handle actions. That's the whole point of Redux.

TodoItem.rename您应该编写处理操作的 reducer,而不是将方法放到模型上(例如)。这就是 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)
      ];
  }
}

If this still doesn't make sense please read through the Redux basics tutorialbecause you seem to have a wrong idea about how Redux applications are structured.

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

回答by adailey

Dan's answer is of course correct in terms of how you should use redux when writing an application from scratch. My answer is based on a non-ideal motivating situation that may be similar to the OPs situation and how I am thinking of dealing with it.

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

I find myself trying create a redux application that depends on 3rd party libraries within the redux reducer that perform operations on input objects with methods, and I wanted to keep these input objects in the redux state. Here is my motivating situation in pseudo-code, and how I am "solving" it using lodash's assign method:

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

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

So this example shows one way of incorporating objects with methods in the redux state as plain objects, and then adding back the methods so they can be used within the redux reducer. Would love to hear Dan's or anyone else's thoughts on this pattern or how it could be done better. I've seen Dan post in several places that storing objects with methods is a bad idea in redux, so the goal here is to find a way to store the object as a plain object and attach the methods when you need them in an efficient way.

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

回答by Mike Richardson

I'm not sure this is exactly relevant (the context is using Redux with React), but I found this page while searching for something similar, so I'm writing this in case it is of interest.

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

My motivation is, I have have state objects where I would like to expose a representation of that state rather than the state itself. So for example (using Immutable.js), I have a state (a schema) that comprises one or more fields, each identified by a unique identifier and stored in a Map, plus a List of identifiers that gives the field ordering.

我的动机是,我有状态对象,我想在其中公开该状态的表示而不是状态本身。因此,例如(使用 Immutable.js),我有一个包含一个或多个字段的状态(架构),每个字段由一个唯一标识符标识并存储在 Map 中,以及一个提供字段排序的标识符列表。

I really don't want to writing stuff like

我真的不想写这样的东西

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

to get the nth field anywhere other than close to the reducer, so that the internal representation is hidden and can be changed easily.

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

What I'm currently doing (and this is a bit of an experiment), is to add a function in the same file as the reducer:

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

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

This is used in the corresponding React components (along with a similarly motivated schema.bindActionCreators function):

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

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

Now I can access the fields, in the correct order, inside the component simply as this.props.getOrderedFields()and not worry about the underlying representation. It also has the additional nice property that it is easy to do dependency injection of the getOrderedFields() function.

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

Also, since I have a similar structure for the field state, I can extend this:

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

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