Created
April 28, 2022 09:40
-
-
Save iShawnWang/529e90dd32980ab6458b6ae444f784b4 to your computer and use it in GitHub Desktop.
ES6 Proxy Immer.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
// 简化版本