statty
Version:
A tiny and unobtrusive state management library for React and Preact apps
184 lines (153 loc) • 5.31 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('prop-types')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', 'prop-types'], factory) :
(factory((global.statty = {}),global.React,global.PropTypes));
}(this, (function (exports,react,PropTypes) { 'use strict';
PropTypes = PropTypes && PropTypes.hasOwnProperty('default') ? PropTypes['default'] : PropTypes;
var isShallowEqual = function isShallowEqual (a, b) {
if (a === b) { return true }
for (var i in a) { if (!(i in b)) { return false } }
for (var i in b) { if (a[i] !== b[i]) { return false } }
return true
};
var isObjectNotNull = function (a, b) {
return (
typeof a === 'object' && typeof b === 'object' && a !== null && b !== null
)
};
var State = (function (Component$$1) {
function State (props, context) {
Component$$1.call(this, props, context);
this.broadcast = context.__statty__.broadcast;
this.inspect = context.__statty__.inspect;
this.state = props.state
? props.state
: props.select(this.broadcast.getState());
this.update = this.update.bind(this);
this.setStateIfNeeded = this.setStateIfNeeded.bind(this);
}
if ( Component$$1 ) State.__proto__ = Component$$1;
State.prototype = Object.create( Component$$1 && Component$$1.prototype );
State.prototype.constructor = State;
State.prototype.update = function update (updaterFn) {
if (this.props.state) {
this.setState(updaterFn);
} else {
var oldState = this.broadcast.getState();
var nextState = updaterFn(oldState);
this.inspect && this.inspect(oldState, nextState, updaterFn);
this.broadcast.setState(nextState);
}
};
State.prototype.setStateIfNeeded = function setStateIfNeeded (nextState) {
var oldSelectdedState = this.state;
var newSelectedState = this.props.select(nextState);
if (
!isObjectNotNull(oldSelectdedState, newSelectedState) ||
!isShallowEqual(oldSelectdedState, newSelectedState)
) {
this.setState(newSelectedState);
}
};
State.prototype.componentDidMount = function componentDidMount () {
if (!this.props.state) {
this.subscriptionId = this.broadcast.subscribe(this.setStateIfNeeded);
// To handle the case where a child component may have triggered a state change in
// its cWM/cDM, we have to re-run the selector and maybe re-render.
this.setStateIfNeeded(this.broadcast.getState());
}
};
State.prototype.componentWillUnmount = function componentWillUnmount () {
this.subscriptionId && this.broadcast.unsubscribe(this.subscriptionId);
};
State.prototype.render = function render () {
return (
this.props.render(
this.props.select(
this.props.state ? this.state : this.broadcast.getState()
),
this.update
) || null
)
};
return State;
}(react.Component));
State.defaultProps = {
select: function (s) { return s; },
render: function () { return null; }
};
State.contextTypes = {
__statty__: PropTypes.object.isRequired
};
State.propTypes = {
state: PropTypes.object,
render: PropTypes.func,
select: PropTypes.func
};
function createBroadcast (initialState) {
var listeners = {};
var id = 1;
var _state = initialState;
function getState () {
return _state
}
function setState (state) {
_state = state;
var keys = Object.keys(listeners);
var i = 0;
var len = keys.length;
for (; i < len; i++) {
// if a listener gets unsubscribed during setState we just skip it
if (listeners[keys[i]]) { listeners[keys[i]](state); }
}
}
// subscribe to changes and return the subscriptionId
function subscribe (listener) {
if (typeof listener !== 'function') {
throw new Error('listener must be a function.')
}
var currentId = id;
listeners[currentId] = listener;
id += 1;
return currentId
}
// remove subscription by removing the listener function
function unsubscribe (id) {
listeners[id] = undefined;
}
return { getState: getState, setState: setState, subscribe: subscribe, unsubscribe: unsubscribe }
}
var Provider = (function (Component$$1) {
function Provider (props, context) {
Component$$1.call(this, props, context);
this.broadcast = createBroadcast(props.state);
}
if ( Component$$1 ) Provider.__proto__ = Component$$1;
Provider.prototype = Object.create( Component$$1 && Component$$1.prototype );
Provider.prototype.constructor = Provider;
Provider.prototype.getChildContext = function getChildContext () {
return {
__statty__: {
broadcast: this.broadcast,
inspect: this.props.inspect
}
}
};
Provider.prototype.render = function render () {
var children = this.props.children;
return Array.isArray(children) ? children[0] : children
};
return Provider;
}(react.Component));
Provider.childContextTypes = {
__statty__: PropTypes.object
};
Provider.propTypes = {
inspect: PropTypes.func,
state: PropTypes.object.isRequired,
children: PropTypes.node.isRequired
};
exports.Provider = Provider;
exports.State = State;
Object.defineProperty(exports, '__esModule', { value: true });
})));