immer 구현해보기 - OOP 버전
2019년 12월 1일에 작성한 포스트입니다.
immer 구현해보기에서 작성된 코드를 OOP 버전으로 리팩토링한 코드이다.
js
const isArray = value => Array.isArray(value)
const assign = (...obj) => Object.assign(...obj)
const canProduce = value => {
return value === undefined || value === null ?
false :
isArray(value) || typeof value === 'object'
}
const shallowCopy = obj => {
return !canProduce(obj) ?
obj :
isArray(obj) ?
obj.concat() :
assign({}, obj)
}
class LinkedList {
constructor(base, parent, propName) {
this.base = base
this.parent = parent
this.propName = propName
this.copy = null
}
toBase() {
return this.copy || this.base
}
changeLinkedList (propName, value) {
const nextValue = {[propName]: value}
this.copy ?
assign(this.copy, nextValue) :
assign(this, {
copy: assign(shallowCopy(this.base), nextValue)
})
if (this.parent) {
this.parent.changeLinkedList(this.propName, this.copy)
}
}
static create(base, parent = null, propName = null) {
return new LinkedList(base, parent, propName)
}
}
class LinkedListProxy {
constructor(base, parentState, propName) {
const {proxy, revoke} = this.createProxy(base)
this.proxy = proxy
this.revokeFn = revoke
this.state = LinkedList.create(base, parentState, propName)
this.children = []
}
createProxy(base) {
return Proxy.revocable(base, {
get: (...args) => this.getter(...args),
set: (...args) => this.setter(...args)
})
}
getter(target, propName) {
const value = this.toBase()[propName]
return canProduce(value) ?
this.createChildProxy(value, propName) :
value
}
createChildProxy(value, propName) {
const child = LinkedListProxy.create(value, this.state, propName)
this.children.push(child)
return child.proxy
}
setter(target, propName, value) {
this.state.changeLinkedList(propName, value)
}
revoke() {
this.revokeFn()
this.children.forEach(child => child.revoke())
}
toBase() {
return this.state.toBase()
}
toProxy() {
return this.proxy
}
static create(base, parentState, propName) {
return new LinkedListProxy(base, parentState, propName)
}
}
const produceBase = (base, fn) => {
const linkedListProxy = LinkedListProxy.create(base)
fn(linkedListProxy.toProxy())
linkedListProxy.revoke()
return linkedListProxy.toBase()
}
const produce = (fn) => (base) => {
return canProduce(base) ? produceBase(base, fn) : base
}