Last active
August 29, 2015 14:27
-
-
Save a-s-o/1ecdb5584a099ec9cc3c to your computer and use it in GitHub Desktop.
Lifecycle hooks for mithril components
This file contains hidden or 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
m.createComponent = function createClass (displayName, opts) { | |
const hooks = _.defaults(opts, hookDefaults); // Apply some defaults (excluded) | |
const methods = _.methods(_.omit(opts, hookNames)); // Get instance methods | |
const propTypes = createValidator(hooks.propTypes, displayName); // Create a prop type validator (excluded) | |
const Component = { | |
controller () { | |
const initialState = hooks.getInitialState() || {}; | |
const ctrl = { | |
defaultProps: hooks.getDefaultProps() || {}, | |
state: initialState, | |
prevState: initialState, | |
setState (obj) { | |
ctrl.prevState = ctrl.state; | |
ctrl.state = _.extend({}, ctrl.state, obj); | |
}, | |
render (nextProps, ...args) { | |
if (ctrl.isMounted !== true) { | |
// Set initial props so they can be copied | |
// over to prevProps on first run | |
ctrl.props = nextProps; | |
// Create the config fn | |
ctrl.config = makeConfig(ctrl, hooks); | |
} else { | |
hooks.componentWillUpdate(ctrl, nextProps); | |
} | |
// Save a copy of previous props | |
ctrl.prevProps = ctrl.props; | |
ctrl.props = nextProps; | |
// Call the component's render method and get the view output | |
const output = hooks.render.apply(wrapper, [ctrl, ...args]); | |
// If top level element then wrap the config | |
if (_.has(output, 'attrs')) { | |
wrapConfig(output.attrs, ctrl.config); | |
return output; | |
} | |
// Wrap all other content | |
// ex: when output = m.component(SomeComponent) | |
// FIX: after https://github.com/lhorie/mithril.js/issues/761 | |
return m('span', { config: ctrl.config }, output); | |
} | |
}; | |
function wrapper (fn) { | |
return function wrapped () { | |
return fn.apply(wrapper, [ctrl, ...arguments]); | |
}; | |
} | |
_.each(methods, function addMethods (methodName) { | |
ctrl[methodName] = wrapper(opts[methodName]); | |
}); | |
hooks.componentWillMount(ctrl); | |
return ctrl; | |
}, | |
view (ctrl, params, children) { | |
let nextProps; | |
if (__DEV__) { | |
nextProps = validateProps(displayName, propTypes, params, children); | |
} else { | |
nextProps = params; | |
nextProps.children = children; | |
} | |
_.defaults(nextProps, ctrl.defaultProps); | |
if (hooks.shouldComponentUpdate(ctrl, nextProps) === false) { | |
return { subtree: 'retain' }; | |
} | |
const args = []; | |
const len = arguments.length; | |
for (let i = 2; i < len; i++) args[i - 2] = arguments[i]; | |
return ctrl.render(nextProps, ...args); | |
} | |
}; | |
return _.extend(Component, hooks.statics); | |
}; | |
// Wraps a config function | |
function wrapConfig (attrs, config) { | |
const original = attrs.config; | |
if (!original) { | |
attrs.config = config; | |
} else { | |
attrs.config = (a, b, c, d) => { | |
if (original) original(a, b, c, d); | |
config(a, b, c, d); | |
}; | |
} | |
} | |
// Makes a config function which calls dom lifecycle hooks | |
function makeConfig (ctrl, hooks) { | |
return function config (element, isInitialized, ctx, vdom) { | |
if (!isInitialized) { | |
hooks.componentDidMount(ctrl, element, vdom); | |
ctrl.isMounted = true; | |
ctx.onunload = _.wrap(ctx.onunload, (originalUnload) => { | |
if (originalUnload) originalUnload(); | |
hooks.componentWillUnmount(ctrl, element, vdom); | |
}); | |
} else { | |
hooks.componentDidUpdate(ctrl, ctrl.prevProps, ctrl.prevState); | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage: