Skip to content

Instantly share code, notes, and snippets.

@JorgenEvens
Created August 19, 2015 07:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JorgenEvens/a9269c0866b554bdb5a0 to your computer and use it in GitHub Desktop.
Save JorgenEvens/a9269c0866b554bdb5a0 to your computer and use it in GitHub Desktop.
A React composite component that loads properties for us and prevents re-rendering until it changes.
import _ from 'lodash';
import React from 'react';
const debug = false;
const log = debug ? console.log.bind(console) : () => {};
const getPath = ( obj, path, defaultValue ) => {
_.each( path.split('.'), function( key ) {
if( _.isUndefined( obj ) )
return false;
if( obj === null )
return false;
if( _.isFunction( obj[key] ) )
obj = obj[key]();
else
obj = obj[key];
});
if( _.isUndefined( obj ) )
return defaultValue;
return obj;
};
export default function Aware( options, Component, propKey, overwriteOptions = null ) {
options = _.merge({
storeMethod: 'get',
actionMethod: 'get',
isInteger: true
}, options, overwriteOptions );
const {
property,
store, storeMethod,
actions, actionMethod,
isInteger
} = options;
const getId = ( obj ) => {
var id = getPath(obj.props, propKey) || getPath(obj, propKey);
if( _.isFunction( id ) )
id = id.call(obj);
if( isInteger )
return parseInt( id, 10 ) || false;
return id || false;
};
var contextTypes = {};
if( propKey.indexOf( 'context.' ) == 0 )
contextTypes = { [propKey.split('.')[1] ]: React.PropTypes.any };
return class extends React.Component {
static contextTypes = contextTypes;
constructor() {
super();
this.state = { entity: null };
}
request( id ) {
log(`request[${property}] `, id);
if( _.isFunction( actionMethod ) )
return actionMethod( id );
return actions[actionMethod]( id );
}
get( id ) {
log(`get[${property}] `, id);
if( _.isFunction( storeMethod ) )
return storeMethod( id );
return store[storeMethod]( id );
}
ready() {
return !!this.state.entity;
}
_update = () => {
this.setState({
entity: this.get( getId(this) )
});
}
componentWillMount() {
const entityId = getId( this );
log(`component will mount[${property}]`, entityId, entityId !== false);
if( entityId !== false )
this.request( entityId );
}
componentWillReceiveProps( props, state ) {
const nextId = getId({
props,
state,
context: this.context
});
const shouldRequest = !this.ready() ||
(nextId !== false && getId( this ) != nextId);
log(`will receive props[${property}]`, nextId, shouldRequest, this);
if( shouldRequest )
this.request(nextId);
}
componentDidMount() {
store.addChangeListener(this._update);
}
componentWillUnmount() {
store.removeChangeListener(this._update);
}
shouldComponentUpdate( props, state ) {
log(`shouldComponentUpdate[${property}] `, !!state.entity);
return !!state.entity;
}
render() {
const props = {
...this.props,
[property]: this.state.entity
};
log(`props[${property}]`, props);
return <Component {...props} />;
}
};
}
"use strict";
import _ from 'lodash';
import { User as UserStore } from 'stores';
import { User as UserActions } from 'actions';
import { BaseAware } from 'composite';
const aware = _.partial( BaseAware, {
property: 'user',
store: UserStore,
actions: UserActions
});
export default aware;
export function ensure(...args) {
return function( classDefinition ) {
return aware( classDefinition, ...args );
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment