Once we've created it and it never changes
rejecting side-effects
...
when you refuse to mutate objects, you have to create a whole new object ( deep clone )
each time something changes
比如:
🐂 | 🐑 | 🐶 | 🐱 | 🐰 | 🐒 |
---|
0
1
2
3
4
🐂 | 👽 | 🐶 | 🐱 | 🐰 | 🐒 |
---|
0
1
2
3
4
5
5
🐂 | 🐑 |
---|
0
1
2
3
4
🐶 | 🐱 |
---|
🐰 | 🐒 |
---|
5
🐂 | 👽 |
---|
0
1
no new API to learn
Automatically freezes
API:
produce(currentState, producer: (draftState) => void): nextState
import produce from "immer"
const baseState = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
]
const nextState = produce(baseState, draftState => {
draftState.push({ todo: "Tweet about it" })
draftState[1].done = true
})
console.log(nextState.length, baseState.length) // 3 2
console.log(baseState === nextState) // false
console.log(baseState[0] === nextState[0]) // true , because not change
console.log(baseState[1] === nextState[1]) // false , because change
show case
Currying
// mapper will be of signature (state, index) => state
const mapper = produce((draft, index) => {
draft.index = index
})
// example usage
console.dir([{}, {}, {}].map(mapper)) // [{index: 0}, {index: 1}, {index: 2}])
In React
/**
* Classic React.setState with a deep merge
*/
onBirthDayClick1 = () => {
this.setState(prevState => ({
user: {
...prevState.user,
age: prevState.user.age + 1
}
}))
}
/**
* ...But, since setState accepts functions,
* we can just create a curried producer and further simplify!
*/
onBirthDayClick2 = () => {
this.setState(
produce(draft => {
draft.user.age += 1
})
)
}
setState can accept function!
Immer will fallback to an ES5 compatible implementation
Object.defineProperty()
。。。
In our project: