Last active
April 3, 2020 20:58
-
-
Save seivan/329dc16e2febf6ef9eeec4c985fffcab to your computer and use it in GitHub Desktop.
Homemade observable
This file contains 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
import React, { Component } from "react"; | |
import ReactDOM from "react-dom"; | |
import { store, observer } from "./konteks"; | |
import "./index.css"; | |
const state = store({ | |
text: "", | |
number: 0, | |
increment: () => state.number++, | |
decrement: () => state.number--, | |
reset: () => (state.number = 0) | |
}); | |
//<pre>{JSON.stringify(state, null, 2)}</pre> | |
const App = () => { | |
return observer(() => { | |
return ( | |
<> | |
<button onClick={() => state.reset()}>Reset</button> | |
<button onClick={() => state.decrement()}> | |
{" "} | |
current: {state.number} - 1{" "} | |
</button> | |
<button onClick={() => state.increment()}> | |
{" "} | |
current: {state.number} + 1{" "} | |
</button> | |
<input | |
placeholder="type something" | |
onChange={e => (state.text = e.target.value)} | |
value={state.text} | |
/> | |
</> | |
); | |
}); | |
}; | |
ReactDOM.render(<App />, document.getElementById("root")); |
This file contains 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
import { useState, useEffect, useCallback, useRef } from "react"; | |
if (!Object.prototype.forEach) { | |
Object.defineProperty(Object.prototype, "forEach", { | |
value: function(callback, thisArg) { | |
if (this == null) { | |
throw new TypeError("Not an object"); | |
} | |
thisArg = thisArg || window; | |
for (var key in this) { | |
if (this.hasOwnProperty(key)) { | |
callback.call(thisArg, this[key], key, this); | |
} | |
} | |
} | |
}); | |
} | |
const objectToComponents = {}; | |
const componentToObjects = {}; | |
let currentlyRenderingComponent; | |
const handler = { | |
get: function(target, key) { | |
if (typeof currentlyRenderingComponent === "undefined") { | |
return target[key]; | |
} | |
let name = currentlyRenderingComponent.name; | |
let components = objectToComponents[target._id]; | |
let objects = componentToObjects[name]; | |
if (components === undefined) { | |
objectToComponents[target._id] = { [name]: currentlyRenderingComponent }; | |
} else if (components[name] === undefined) { | |
components[name] = currentlyRenderingComponent; | |
} | |
if (objects === undefined) { | |
componentToObjects[name] = { [target._id]: true }; | |
} else if (objects[target._id] !== true) { | |
objects[target._id] = true; | |
} | |
currentlyRenderingComponent = undefined; | |
return target[key]; | |
}, | |
set: function(target, key, value) { | |
target[key] = value; | |
const components = objectToComponents[target._id]; | |
if (components != null) { | |
components.forEach(component => { | |
component.invalidate(); | |
}); | |
} | |
return true; | |
} | |
}; | |
const getNextId = () => { | |
return `${Math.floor(Math.random() * 10e9)}`; | |
}; | |
export function store(object) { | |
const obj = { ...object, ...{ _id: getNextId } }; | |
return new Proxy(obj, handler); | |
} | |
var x = 0; | |
export const observer = fn => { | |
const name = `observer(${getNextId()})`; | |
const reaction = useRef(null); | |
//const forceUpdate = useForceUpdate(); | |
const [, setTick] = useState(0); | |
const update = useCallback(() => { | |
setTick(tick => tick + 1); | |
}, []); | |
useEffect(() => { | |
return () => { | |
console.log("UNMOUNTED"); | |
const objects = componentToObjects[name]; | |
if (objects !== undefined) { | |
componentToObjects[name] = undefined; | |
objects.forEach(component => { | |
objectToComponents[component._id] = undefined; | |
}); | |
} | |
}; | |
}, []); | |
if (!reaction.current) { | |
reaction.current = { | |
name: name, | |
invalidate: () => { | |
console.log("invalidate"); | |
update(); | |
}, | |
render: () => { | |
return fn(); | |
} | |
}; | |
} | |
currentlyRenderingComponent = reaction.current; | |
return reaction.current.render(); | |
}; | |
This file contains 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
export const Observer = props => { | |
const nameRef = useRef(); | |
const reaction = useRef(null); | |
const [_, setTick] = useState({}); | |
const update = useCallback(vaz => { | |
setTick(old => { | |
return { ...old, ...vaz }; | |
}); | |
}, []); | |
useEffect(() => { | |
console.log("EFFECT"); | |
return () => { | |
console.log("UNMOUNTED"); | |
const name = nameRef.current; | |
const objects = componentToObjects[name]; | |
if (objects !== undefined) { | |
componentToObjects[name] = undefined; | |
objects.forEach(component => { | |
objectToComponents[component._id] = undefined; | |
}); | |
} | |
}; | |
}, []); | |
if (!reaction.current) { | |
console.log("ONCE"); | |
nameRef.current = `observer(${getNextId()})`; | |
const name = nameRef.current; | |
reaction.current = { | |
name: name, | |
invalidate: newState => { | |
// or update(newState) | |
setTick(old => { | |
return { ...old, ...newState }; | |
}); | |
} | |
}; | |
} | |
currentlyRenderingComponent = reaction; | |
console.log("render"); | |
return props.children(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment