produce.ts
ts
const clone = function <T>(obj: T): T {
return JSON.parse(JSON.stringify(obj))
}
const produce = function <T>(fn: (state: T, ...options: any[]) => void) {
return (state: T, ...options: any[]): T => {
const clonedState = clone(state)
fn(clonedState, ...options)
return clonedState
}
}
const baseState: any[] = [
{
todo: "Learn typescript",
done: true
},
{
todo: "Try immer",
done: false
}
]
const nextState = produce((draftState: any[]) => {
draftState.push({todo: "Tweet about it"})
draftState[1].done = true
})(baseState)
console.log(baseState.length === 2)
console.log(nextState.length === 3)
console.log(baseState[1].done === false)
console.log(nextState[1].done === true)
console.log(nextState[0] === baseState[0])
console.log(nextState[1] !== baseState[1])
produce((draftState: {x : number }) => {
draftState.x = 10
})({ x: 0 })
produce-simple.js
js
const clone = json => JSON.parse(JSON.stringify(json))
const produce = (state, recipe) => {
const clonedState = clone(state)
recipe(clonedState)
return clonedState
}
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(baseState.length === 2) // true
console.log(nextState.length === 3) // true
console.log(baseState[1].done === false) // true
console.log(nextState[1].done === true) // true
console.log(nextState[0] === baseState[0]) // false
console.log(nextState[1] !== baseState[1]) // true
persistant-unit/linked-list.js
js
/*
base: 오리지날 데이터
state: linkedList 아이템
list: state[]
*/
const toLinkedListItem = (base, parent = null, propName = null) => {
return {
base,
parent,
propName,
copy: null,
}
}
const toLinkedList = (base, parent = null, propName = null, list = []) => {
const state = toLinkedListItem(base, parent, propName)
list.push(state)
for (const propName in base) {
if (typeof base[propName] === 'object') {
toLinkedList(base[propName], state, propName, list)
}
}
if (parent) {
return state
} else {
return list
}
}
/*
첫번째 아이템이 copy가 있으면 변경된 것을 의미함
*/
const changeLinkedList = (state, propName, value) => {
if (state.copy) {
state.copy[propName] = value
} else {
state.copy = Object.assign({}, state.base, {[propName]: value})
}
if (state.parent) {
changeLinkedList(state.parent, state.propName, state.copy)
}
}
const toBase = (list) => {
return list[0].copy ? list[0].copy : list[0].base
}
persistant-unit/proxy.js
js
const createProxy = target => {
const handler = {
get (target, key) {
console.log('GET', key)
return target[key]
},
set (target, key, value) {
console.log('SET', key, value)
target[key] = value
}
}
return new Proxy(target, handler)
}
const target = {
message: ''
}
const proxy = createProxy(target)
proxy.message = 'World!'
console.log(proxy.message)
console.group('Destructuring')
const {message} = proxy
console.log(message)
console.groupEnd()
persistant-unit/proxy-all.js
js
const createProxy = (state, revokes) => {
const handler = {
get (target, key) {
const value = target[key]
if (typeof value === 'object') {
const {proxy} = createProxy(value, revokes)
return proxy
} else {
return value
}
},
set (target, key, value) {
console.log('SET', key, value)
target[key] = value
}
}
const {proxy, revoke} = Proxy.revocable(state, handler)
revokes.push(revoke)
return {proxy, revoke, revokes}
}
const proxyAll = (base, fn) => {
const {proxy, revokes} = createProxy(base, [])
fn(proxy)
revokes.forEach(fn => fn())
return base
}
persistant-unit/proxy-revoke.js
js
const createProxy = target => {
const handler = {
get (target, key) {
console.log('GET', key)
return target[key]
},
set (target, key, value) {
console.log('SET', key, value)
target[key] = value
}
}
return Proxy.revocable(target, handler)
}
const target = {
message: ''
}
const {proxy, revoke} = createProxy(target)
proxy.message = 'World!'
console.log(proxy.message)
revoke()
console.log(proxy.message)
persistant/produce.js
js
const isArray = value => Array.isArray(value)
const canProduce = value => {
return value === undefined || value === null ?
false :
isArray(value) || typeof value === 'object'
}
const assign = (...obj) => Object.assign(...obj)
const shallowCopy = obj => {
if (!canProduce(obj)) return obj
if (isArray(obj)) return obj.concat()
return assign({}, obj)
}
const toLinkedListItem = (base, parent = null, propName = null) => {
return {
base,
parent,
propName,
copy: null,
}
}
const toBase = (state) => {
return state.copy ? state.copy : state.base
}
const changeLinkedList = (state, propName, value) => {
const nextValue = {[propName]: value}
state.copy ?
assign(state.copy, nextValue) :
assign(state, {
copy: assign(shallowCopy(state.base), nextValue)
})
if (state.parent) {
changeLinkedList(state.parent, state.propName, state.copy)
}
}
const createProxy = (base, revokes, parentState, propName) => {
const state = toLinkedListItem(base, parentState, propName)
const handler = {
get (target, key) {
const value = toBase(state)[key]
if (canProduce(value)) {
const {proxy} = createProxy(value, revokes, state, key)
return proxy
} else {
return value
}
},
set (target, key, value) {
changeLinkedList(state, key, value)
}
}
const {proxy, revoke} = Proxy.revocable(base, handler)
revokes.push(revoke)
return {proxy, revoke, revokes, state}
}
const produceBase = (base, fn) => {
const {proxy, revokes, state} = createProxy(base, [])
fn(proxy)
revokes.forEach(fn => fn())
return toBase(state)
}
const produce = (fn) => (base) => {
return canProduce(base) ? produceBase(base, fn) : base
}
module.exports = {produce}
persistant-merge/produce.js
js
const toLinkedListItem = (base, parent = null, propName = null) => {
return {
base,
parent,
propName,
copy: null,
}
}
const changeLinkedList = (state, propName, value) => {
if (state.copy) {
state.copy[propName] = value
} else {
state.copy = Object.assign({}, state.base, {[propName]: value})
}
if (state.parent) {
changeLinkedList(state.parent, state.propName, state.copy)
}
}
const createProxy = (base, revokes, parentState, propName) => {
const state = toLinkedListItem(base, parentState, propName)
const handler = {
get (target, key) {
const value = state.copy ? state.copy[key] : state.base[key]
if (typeof value === 'object') {
const {proxy} = createProxy(value, revokes, state, key)
return proxy
} else {
return value
}
},
set (target, key, value) {
console.log('SET', target, key, value)
changeLinkedList(state, key, value)
}
}
const {proxy, revoke} = Proxy.revocable(base, handler)
revokes.push(revoke)
return {proxy, revoke, revokes, state}
}
const produce = (base, fn) => {
const {proxy, revokes, state} = createProxy(base, [])
fn(proxy)
revokes.forEach(fn => fn())
return state.copy ? state.copy : state.base
}
persistant-oop/produce.js
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
}
module.exports = {produce}