Skip to content

Instantly share code, notes, and snippets.

@jeffmcmahan
Last active May 5, 2020 10:54
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffmcmahan/8d10c579df82d32b13e2f449d1575c95 to your computer and use it in GitHub Desktop.
Save jeffmcmahan/8d10c579df82d32b13e2f449d1575c95 to your computer and use it in GitHub Desktop.
import {reconcile} from './utils/reconcile.mjs'
import {bar} from './bar-component.mjs'
import {baz} from './baz-component.mjs'
export const app = () => {
/// This is a component; it creates a node, sets some invariant properties on
/// that node (e.g., handler functions, sometimes CSS classes, &c.).
/// => any => Object
const node = document.createElement('div')
node.onclick = evt => {
evt.stopPropagation()
alert('Foo!')
}
node.oncontextmenu = evt => {
evt.preventDefault()
evt.stopPropagation()
}
return props => {
/// Here's our update function, which generates an object whose properties
/// will be applied to the node if they differ from it.
/// => Object
let className = 'foo'
if (props.inactive) {
className += ' inactive'
}
// Okay, let's return our partial state description:
return {
node,
className,
childNodes: [
bar(props), // These components are of the same form as the parent.
baz(props)
]
}
}
}
// (1) Let's create the app component:
const myApp = app()
// (2) Let's call it to get a state object with a DOM node reference:
const appState = myApp({inactive: false})
// (3) Let's apply the state properties to the app DOM node:
reconcile(appState)
// (4) Let's mount the app to the DOM:
document.body.append(appState.node)
// And here's our global render function:
window.render = props => reconcile(myApp(props))
// For example:
render({inactive: true}) // className in DOM is now 'foo inactive'
@jeffmcmahan
Copy link
Author

jeffmcmahan commented Jan 12, 2020

The logic of a reconcile function is veerrrrryy roughly:

export const reconcile = ({node, ...state}) => {
    Object.keys(state).forEach(prop => {
        if (node[prop] != state[prop]) node[prop] = state[prop]
    })
}

Probably my favorite thing about this is that reconcile is different in like every project I do; sometimes it's 100% asynchronous with all kinds of hooks for doing complex animations from within component code, and sometimes is plain Jane - much closer to the above, taking maybe 50-60 lines to cover the ground without frills. I agree with Knuth in hating "toolkits" that don't give you this kind re-editability, which is to say, freedom.

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