immer: 不可变对象
一个可以提升性能的小工具
安装
# 原生 immer / 还有一个 hook(并不是必须的)
yarn add immer use-immer
使用 userState + immer
引用这一句:
import { produce } from "immer";
import React, { useState } from 'react';
import { produce } from 'immer';
const TodoList = () => {
const [todos, setTodos] = useState([
{ id: 'React', title: 'Learn React', done: true },
{ id: 'Immer', title: 'Try Immer', done: false }
]);
const handleToggle = (id) => {
setTodos(
produce((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done;
})
);
};
const handleAdd = () => {
setTodos(
produce((draft) => {
draft.push({
id: 'todo_' + Math.random(),
title: 'A new todo',
done: false
});
})
);
};
return (
<div>
<ul>
{todos.map((todo) => (
<li
key={todo.id}
onClick={() => handleToggle(todo.id)}
style={{
textDecoration: todo.done ? 'line-through' : ''
}}>
{todo.title}
</li>
))}
</ul>
<button onClick={handleAdd}>Add Todo</button>
</div>
);
};
export default TodoList;
使用 useImmer
- 初始化
data
update
数据- 参考这里: https://immerjs.github.io/immer/example-setstate
初始化
useImmer
,并传入数据
const [todos, setTodos] = useImmer([
{
id: "React",
title: "Learn React",
done: true,
},
{
id: "Immer",
title: "Try Immer",
done: false,
},
]);
更新 todos
const handleToggle = (id) => {
setTodos((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done;
});
};
完整代码如下
import React, { useCallback } from 'react';
import { useImmer } from 'use-immer';
const TodoList = () => {
const initialState = [
{ id: 'React', title: 'Learn React', done: true },
{ id: 'Immer', title: 'Try Immer', done: false }
];
const [todos, setTodos] = useImmer(initialState);
const handleToggle = useCallback((id) => {
setTodos((draft) => {
const todo = draft.find((todo) => todo.id === id);
todo.done = !todo.done;
});
}, []);
const handleAdd = useCallback(() => {
setTodos((draft) => {
draft.push({
id: 'TodoList',
title: 'Try TodoList',
done: false
});
});
}, []);
return (
<div>
<ul>
{todos.map((todo) => (
<li
key={todo.id}
onClick={() => handleToggle(todo.id)}
style={{
textDecoration: todo.done ? 'line-through' : ''
}}>
{todo.title}
</li>
))}
</ul>
<button onClick={handleAdd}>Add Todo</button>
</div>
);
};
export default TodoList;
使用 userReducer + immer
import { useReducer } from 'react';
import { produce } from 'immer';
const TodoList = () => {
const initialState = [
{ id: 1, text: 'Learn React', completed: false },
{ id: 2, text: 'Learn Redux', completed: false }
];
const reducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return produce(state, (draftState) => {
draftState.push(action.payload);
});
case 'TOGGLE_TODO':
return produce(state, (draftState) => {
const todo = draftState.find((todo, index) => index === action.payload);
todo.completed = !todo.completed;
});
default:
return state;
}
};
const [todos, dispatch] = useReducer(reducer, initialState);
const toggleTodo = (index) => dispatch({ type: 'TOGGLE_TODO', payload: index });
const handleAdd = () => {
dispatch({
type: 'ADD_TODO',
payload: {
id: 3,
text: 'Learn Immer',
completed: false
}
});
};
return (
<div>
<ul>
{todos.map((todo, index) => (
<li
key={index}
style={{
textDecoration: todo.completed ? 'line-through' : ''
}}
onClick={() => {
toggleTodo(index);
}}>
{todo.text}
</li>
))}
</ul>
<button onClick={handleAdd}>Add Todo</button>
</div>
);
};
export default TodoList;
使用 userImmerReducer
import { useImmerReducer } from 'use-immer';
const TodoList = () => {
const initialState = [
{ text: 'Learn Hooks', completed: false },
{ text: 'Learn Redux', completed: false }
];
const reducer = (state, action) => {
switch (action.type) {
case 'add-todo':
state.push({ text: action.payload, completed: false });
break;
case 'toggle-todo':
state[action.payload].completed = !state[action.payload].completed;
break;
default:
break;
}
};
// useImmmerReducer
const [todos, dispatch] = useImmerReducer(reducer, initialState);
const handleAdd = () => dispatch({ type: 'add-todo', payload: 'new todo' });
const toggleTodo = (index) => dispatch({ type: 'toggle-todo', payload: index });
return (
<div>
<ul>
{todos.map((todo, index) => (
<li
key={index}
style={{ textDecoration: todo.completed ? 'line-through' : '' }}
onClick={() => toggleTodo(index)}>
{todo.text}
</li>
))}
</ul>
<button onClick={handleAdd}>Add Todo</button>
</div>
);
};
export default TodoList;