我正在用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>
}
}
请原谅人为和简单的示例。关键是状态不再是简单的键值存储。它具有一个复杂的对象,该对象试图隐藏原始数据并封装业务逻辑(我知道上面的示例中没有业务逻辑,请随我想象一下)。
我不需要序列化此状态。本地存储中没有时间旅行或恢复状态。
任何人都可以帮助我理解为什么这可能是一种不好的做法吗?
我能想到的优点-
我能想到的缺点-
答案 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是许多开发人员都支持的新出现的不变性库,但是我不太了解它的内部工作方式。