在反应状态下使用复杂对象是否不好?

时间:2019-09-21 01:41:45

标签: javascript reactjs

我正在用React编写一个应用程序,它以组件的状态存储了一些数据。

我最初选择将数据包装在一个函数中,该函数将封装可以对数据执行的所有操作。

为使这个问题更笼统,我将示例作为待办事项展示。我在现实世界中的用例更加复杂。

function Todo(todo = EmptyTodo) {
  // helper function to easily create immutable methods below
  function merge(update) {
    return Todo(Object.assign({}, todo, update))
  }

  return {
    getComplete() { 
      return todo.complete
    },
    getText() { 
      return todo.text 
    },
    toggleComplete() {
      return merge({ complete: !todo.complete })
    },
    setText(text) {
      return merge({ text })
    }
  }
}

这个例子有点不足。理想情况下,一个更好的例子可能不仅仅是获取方法和方法。它可能包含更接近业务逻辑的内容。

继续前进,现在我在像这样的react组件中使用了Todo

class TodoRow extends React.Component {
  state = {
    todo: Todo()
  }

  handleToggleComplete = () => this.setState(state => ({
    todo: state.todo.toggleComplete()
  }))

  handleTextChange = e => {
    const text = e.target.value
    this.setState(state => ({
      todo: state.todo.setText(text)
    }))
  }

  render() {
    return <div>
      <input 
        type="checkbox" 
        onChange={this.handleToggleComplete} 
        value={this.state.todo.getComplete()}
      />
      <input 
        type="text" 
        onChange={this.handleTextChange} 
        value={this.state.todo.getText()}
      />
      {this.state.todo.getText()}
    </div>
  }
}

请原谅人为和简单的示例。关键是状态不再是简单的键值存储。它具有一个复杂的对象,该对象试图隐藏原始数据并封装业务逻辑(我知道上面的示例中没有业务逻辑,请随我想象一下)。

我不需要序列化此状态。本地存储中没有时间旅行或恢复状态。

任何人都可以帮助我理解为什么这可能是一种不好的做法吗?

我能想到的优点-

  • 如果要在其他地方使用待办事项,则可以重新使用数据逻辑。
  • todo对象可以在一处强制/实现不变性。
  • 显示和数据操作之间的分隔。
  • 现在可以使用Todo对象实现多态行为(无法切换的ReadOnlyTodo或 编辑,仅完成的待办事项,但文本不能完成 编辑)而无需修改React组件。

我能想到的缺点-

  • 无法将状态序列化为JSON(可以使用Todo.toJSON()方法轻松修复)。
  • 不遵循setState对键/值对进行操作的正常模式。
  • 调用todo.setText()可能会造成混乱,因为它不会更新状态。

代码笔:Key/Value Store Complex object

1 个答案:

答案 0 :(得分:1)

这可能不是一个好主意,因为不变性的实现很简单,就是通过更改完全重新创建一个新对象。另外,您正在制作浅表副本,在现实生活中的应用程序中,您将意识到嵌套对象是一种常见的做法,尤其是在JSON中。

诸如Immutable.js之类的库会为其数据结构创建内部哈希表,这些哈希表仅重新创建已更改的节点,而不是重新创建整个对象,同时抽象该逻辑以实现有效的不变性。

作为一个例子,假设您有一个具有200个属性的不可变对象。对于每个属性的每次更新,都需要重新创建具有这200个属性的新对象。请注意,根据您的应用程序,输入的简单类型可能会重新创建完整的对象。

// ❗️ Merge would need to reassign all 200 properties for every change.
// ❗️ Plus, it won't work for nested properties as it is a shallow copy:

function merge(update) {
  return Todo(Object.assign({}, todo, update))
}

例如,Immutable.js将创建一个包含20个节点的哈希映射,每个节点包含10个属性。如果更新一个属性,则仅转储并重新创建一个节点,而保留另一个。

还要考虑,如果任何子组件依赖于该状态,则它们的渲染可能会被触发,因为没有备注。这就是为什么存在reselect等Redux库的原因。

注意:immer.js是许多开发人员都支持的新出现的不变性库,但是我不太了解它的内部工作方式。