Skip to content

Instantly share code, notes, and snippets.

@iShawnWang
Created April 28, 2022 09:40
Show Gist options
  • Save iShawnWang/529e90dd32980ab6458b6ae444f784b4 to your computer and use it in GitHub Desktop.
Save iShawnWang/529e90dd32980ab6458b6ae444f784b4 to your computer and use it in GitHub Desktop.
ES6 Proxy Immer.js
const PROXY_STATE = Symbol('immer-proxy-state')
class State {
srcObj
copy
touched
parent
constructor(srcObj, parent) {
this.srcObj = srcObj
this.parent = parent
this.copy = undefined
this.touched = false
}
markChanged() {
if (!this.touched) {
this.touched = true
this.copy = { ...this.srcObj }
if (this.parent) this.parent.markChanged()
}
}
}
const createProxy = (obj, parent) => {
const state = new State(obj, parent)
return new Proxy(state, {
get: (__, prop) => {
if (prop === PROXY_STATE) {
return state
}
state.markChanged()
const src = state.copy
src[prop] = typeof src[prop] === 'object' ? createProxy(src[prop], state) : src[prop]
return src[prop]
},
set: (__, prop, value) => {
state.markChanged()
state.copy[prop] = value
return true
},
})
}
function isProxy(value) {
return !!value && !!value[PROXY_STATE]
}
const finalize = (base) => {
if (!isProxy(base)) {
return base
}
const state = base[PROXY_STATE]
if (state.touched) {
const copy = state.copy
Object.keys(copy).forEach((prop) => {
copy[prop] = finalize(copy[prop])
})
return copy
} else {
return state.base
}
}
const produce = (srcObj, producer) => {
// 1. 创建 proxy 对象, 自动 copy on write
const proxyedObj = createProxy(srcObj)
// 2. (proxyedObj) => { //用户执行修改 return 结果 }
producer(proxyedObj)
// 3. proxy 对象还原原始 js obj 返回
return finalize(proxyedObj)
}
// 测试
const obj = { a: { b: {c: 3} } }
const modified = produce(obj, (draft) => (draft.a.b = {d:4}))
console.log(obj)
console.log(modified)
console.log(obj === modified)
@iShawnWang
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment