如何从 Mobx 可观察数组中删除一个元素,而不导致整个消费组件重新渲染?
How to remove an element from a Mobx observable array, without causing the entire consuming component to rerender?
假设我有一个 todoStore。它有一个通过 id 删除待办事项的操作。请注意,我尝试了过滤器和拼接:
export default class TodosStore {
constructor() {
makeAutoObservable(this)
}
todos = [
{
id: 1,
name: "name1",
completed: true
},
{
id: 15,
name: "name2",
completed: true
},
{
id: 14,
name: "name3",
completed: true
}
]
removeTodo(id) {
// this.todos = this.todos.filter(todo=>todo.id != id)
for (let todo of this.todos) {
if (todo.id == id) {
const indexOf = this.todos.indexOf(todo)
this.todos.splice(indexOf, 1)
}
}
}
};
消费 Todos 组件(注意我用观察者包装了 Todo):
import { combinedStores } from "."
const ObservableTodo = observer(Todo);
export default observer(() => {
const { todosStore } = combinedStores
return (
<div >
{todosStore.todos.map(todo=>{
return(
<ObservableTodo onDelete={()=>{todosStore.removeTodo(todo.id)}} onNameChange={(value)=>{todosStore.editTodoName(todo.id,value)}} key={todo.id} todo={todo}></ObservableTodo>
)
})}
</div>
)
})
简单的 Todo 组件:
export default ({todo,onNameChange,onDelete}) => {
return (
<div style={{padding:'10px',margin:'10px'}}>
<p>ID: {todo.id}</p>
<input onChange={(e)=>{onNameChange(e.target.value)}} value={todo.name}></input>
<p>Completed: {todo.completed ? 'true' : 'false'} <button onClick={onDelete} className="btn btn-danger">Delete</button></p>
</div>
)
}
即使我清楚地改变(而不是构建一个新数组)商店中的 todos 数组,Todos 组件重新呈现(我通过 [=25= 看到它]),
每个剩余的 Todo 组件也是如此。
有什么办法吗?也许我的设置有什么问题吗?我正在使用最新的 Mobx(6) 和 mobx-react。
Todos
组件应该重新渲染,因为它依赖于 todos
数组内容(因为它 map
在它上面)。因此,当您通过添加或删除一些待办事项来更改 todos
内容时 - Todos
组件将重新呈现,因为它需要呈现新内容,新的待办事项列表。
每个 Todo
重新呈现,因为您没有用 observer
包装它。包装每个使用某种可观察状态的组件是一种很好的做法,Todo
显然是这样做的。
您更改了 todo
数组的 length
,因此 map
函数开始运行。然后当您遍历元素时,您正在传递 new
ObservableTodo
组件(onDelete
、onChange
)的属性,这将使 ObservableTodo
始终重新渲染。
即使组件是 Mobx observable,它仍然遵循“React 规则”,当 React 在组件属性中看到 new
引用时,它会渲染组件。
假设我有一个 todoStore。它有一个通过 id 删除待办事项的操作。请注意,我尝试了过滤器和拼接:
export default class TodosStore {
constructor() {
makeAutoObservable(this)
}
todos = [
{
id: 1,
name: "name1",
completed: true
},
{
id: 15,
name: "name2",
completed: true
},
{
id: 14,
name: "name3",
completed: true
}
]
removeTodo(id) {
// this.todos = this.todos.filter(todo=>todo.id != id)
for (let todo of this.todos) {
if (todo.id == id) {
const indexOf = this.todos.indexOf(todo)
this.todos.splice(indexOf, 1)
}
}
}
};
消费 Todos 组件(注意我用观察者包装了 Todo):
import { combinedStores } from "."
const ObservableTodo = observer(Todo);
export default observer(() => {
const { todosStore } = combinedStores
return (
<div >
{todosStore.todos.map(todo=>{
return(
<ObservableTodo onDelete={()=>{todosStore.removeTodo(todo.id)}} onNameChange={(value)=>{todosStore.editTodoName(todo.id,value)}} key={todo.id} todo={todo}></ObservableTodo>
)
})}
</div>
)
})
简单的 Todo 组件:
export default ({todo,onNameChange,onDelete}) => {
return (
<div style={{padding:'10px',margin:'10px'}}>
<p>ID: {todo.id}</p>
<input onChange={(e)=>{onNameChange(e.target.value)}} value={todo.name}></input>
<p>Completed: {todo.completed ? 'true' : 'false'} <button onClick={onDelete} className="btn btn-danger">Delete</button></p>
</div>
)
}
即使我清楚地改变(而不是构建一个新数组)商店中的 todos 数组,Todos 组件重新呈现(我通过 [=25= 看到它]), 每个剩余的 Todo 组件也是如此。
有什么办法吗?也许我的设置有什么问题吗?我正在使用最新的 Mobx(6) 和 mobx-react。
Todos
组件应该重新渲染,因为它依赖于 todos
数组内容(因为它 map
在它上面)。因此,当您通过添加或删除一些待办事项来更改 todos
内容时 - Todos
组件将重新呈现,因为它需要呈现新内容,新的待办事项列表。
每个 Todo
重新呈现,因为您没有用 observer
包装它。包装每个使用某种可观察状态的组件是一种很好的做法,Todo
显然是这样做的。
您更改了 todo
数组的 length
,因此 map
函数开始运行。然后当您遍历元素时,您正在传递 new
ObservableTodo
组件(onDelete
、onChange
)的属性,这将使 ObservableTodo
始终重新渲染。
即使组件是 Mobx observable,它仍然遵循“React 规则”,当 React 在组件属性中看到 new
引用时,它会渲染组件。