Skip to content

Instantly share code, notes, and snippets.

@rphansen91
Last active December 14, 2016 18:25
Show Gist options
  • Save rphansen91/77ecd4615aab7e340738d93905818a64 to your computer and use it in GitHub Desktop.
Save rphansen91/77ecd4615aab7e340738d93905818a64 to your computer and use it in GitHub Desktop.
React From Scratch in ~100 lines
import Component from './toy-react'
import { div, img } from './toy-jsx'
class Nested extends Component {
render () {
const { src, style } = this.props;
return img({ src, style })
}
}
class Example extends Component {
constructor (props) {
super(props)
this.state = { active: false }
}
setActive (active) {
this.setState({ active })
}
render () {
const { srcs } = this.props;
const { active } = this.state;
return (
div({ class: active?'activeClass':'inactiveClass', events: {
mouseenter: this.setActive.bind(this, true),
mouseleave: this.setActive.bind(this, false)
}}, [ ...srcs.map((src) =>
Nested.create({ src })
)])
)
}
}
Example.create({ srcs: ['http://placehold.it/320x320', 'http://placehold.it/640x640'] })
.place(document.getElementById('root'))
HTMLElement.prototype.attr = function (key, val) {
this.setAttribute(key, val);
}
HTMLElement.prototype.attrs = function (attrs) {
for (var attr in attrs) {
this.attr(attr, attrs[attr]);
}
}
HTMLElement.prototype.events = function (events) {
for (var event in events) {
this.addEventListener(event, events[event])
}
}
HTMLElement.prototype.render = function (component, dom) {
if (!component) return;
if (Array.isArray(component)) return childRenderer.call(this, component, dom);
if (dom && dom.placed) return cmpUpdater.call(this, component, dom);
if (typeof component.place === 'function') return cmpRenderer.call(this, component, dom);
if (typeof component === 'string') return textRenderer.call(this, component, dom);
return elementRenderer.call(this, component, dom);
}
function childRenderer (children, dom) {
return children.map((el, i) => this.render(children[i], dom && dom[i]))
}
function cmpRenderer (component) {
// THIS IS A NESTED COMPONENT
return { placed: component.place(this) }
}
function cmpUpdater (component, dom) {
// THIS IS A PREVIOUSLY RENDERED NESTED COMPONENT
dom.placed.update(component.props);
return { placed: dom.placed };
}
function textRenderer (text) {
this.innerHTML = text;
return text;
}
function elementRenderer (component, dom) {
let element;
const attrs = component.attrs || {};
const events = attrs.events || {};
if (dom && dom.element) {
element = dom.element;
element.attrs(attrs);
} else{
element = document.createElement(component.tagName);
element.attrs(attrs);
element.events(events);
this.appendChild(element);
}
return {
element: element,
children: element.render(component.children, dom && dom.children),
remove: () => this.removeChild(element)
}
}
const h = (tagName) => (attrs, children) => ({
tagName,
attrs,
children
})
export const a = h('A');
export const p = h('P');
export const h1 = h('H1');
export const h2 = h('H2');
export const h3 = h('H3');
export const h4 = h('H4');
export const div = h('DIV');
export const img = h('IMG');
export const span = h('SPAN');
import './prototypes'
export default class Component {
constructor (props) {
this.props = props;
}
static create (props) {
// WE CAN INTERNALLY `new` THE INSTANCE
// ALLOWING US TO CALL UPDATE ON SUBSEQUENT RENDERS
return {
props,
place: (root) => new this(props).initialize(root),
}
}
initialize (root) {
// CALLED ONCE WHEN THE COMPONENT IS MOUNTED TO DOM
// SHOULD ADD ERRORS TO MAKE SURE THIS IS NOT CALLED DUPLICATE INSTANCES WILL BE ADDED TO DOM
this.root = root;
this.initiateRender();
return this;
}
setState (state) {
this.state = Object.assign({}, this.state, state);
this.initiateRender();
}
update (props) {
this.props = props || {};
this.initiateRender();
}
initiateRender () {
const html = this.render();
const previousDOM = this.renderedDOM;
// NEED TO PASS IN THE PREVIOUS DOM SO WE DONT CREATE ALEADY CREATED ELEMENTS
this.renderedDOM = this.root.render(html, previousDOM);
}
render () {
console.warn('Must supply a render function');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment