Skip to content

Instantly share code, notes, and snippets.

@aperkaz
Last active January 6, 2018 17:56
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 aperkaz/e8a47e154bdc16f0581e49a9202738e8 to your computer and use it in GitHub Desktop.
Save aperkaz/e8a47e154bdc16f0581e49a9202738e8 to your computer and use it in GitHub Desktop.
JS Decorators in React

JS Decorators in React

Short practical guide to clarify the use cases of Decorators (aka HOF) and High Order Components. The examples is applicable for general JS, but are contructed using ES2015 + React.

Higher Order Functions and Higher Order Components

Higher Order Functions (HOF) are functions that receive functions as arguments (Inception :/) and return functions. They operate over functions instead of "simple" variables. Decorators provide syntactic suggar for implementing HOFs JS, to modify and extend passed functions.

Higher Order Components (HOC) are a React specific patter, very much similar to the aforemntioned HOF. Instead of receiving functions as argumnets, Components are passed. They are especially useful to add stateful wrappers to componets and alter livecycle methods.

Hands On

Short examples of different use cases for Decorators and HOC.

Decorators

They can be added with @.

Property Decorators

Modify the properties of certain object's property.

const readonly = (target, name, descriptor) => {
  descriptor.writable = false;
  return descriptor;
}

class Person {
  @readonly
  name = 'Alain';
}

const me = new Person();

console.log('Person.name => ', me.name);
// My name is: Alain

me.name = 'John';
// Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Person>'

Method Decorators

Modify the calls of certain methods. Provided examples:

  • Override the methods arguments
const overrideMethodArguments = (target, name, descriptor) => {
  const original = descriptor.value;

  descriptor.value = (...args) => {
  	console.log(`Calling ${name} with arguments: ${JSON.stringify(args)}`);
    return original.apply(this, ['OVERRIDE ARG']);
  }
}

class Human {

  @overrideMethodArguments
  getName (name) {
    return name;
  }
};

const h = new Human();
console.log(`Decorated Human.getName() => ${h.getName('myName', 'arg2')}`);
// Decorated Human.getName() => OVERRIDE ARG
  • Add execution time log (visible in browser console)
const logExecutionTime = (target, name, descriptor) => {
	const oldValue = descriptor.value;
  
  descriptor.value = (...args) => {
      console.time(`timeProfiling-${name}`);
      const result = oldValue.apply(this, [...args]);
      console.timeEnd(`timeProfiling-${name}`);
      // timeProfiling-foo: 0.00390625ms
      
    	return result;
   }
};

class Dog {

  @logExecutionTime
  foo () {
    console.log('bark bark');
  }
};

const d = new Dog();

d.foo();
// timeProfiling-foo: 0.257080078125ms
// bark bark

Class Decorators

Decorate ES2015 classes.

console.log('\n3- CLASS DECORATORS');
const superhero = target => {
	target.power = 'A superHero can fly'
  target.terminate = enemy => `${enemy} has been terminated`
};

@superhero
class SuperHero {};

console.log(SuperHero.power);
// A superhero can fly
console.log(SuperHero.terminate('Venom'));
// Venom has been terminated

Higher Order Components

Simple component to change

const Component = ({text = 'default value', otherProp}) => (
	<div>
    <div style={{ color: 'red'}}>text: {text}</div>
    <div style={{ color: 'blue'}}>otherProp: {otherProp}</div>
  </div>
);

ReactDOM.render(<Component text={'dummy text'} />, document.getElementById('app'));

Add property:value to component

const AddPropHOC = Component => {
	const Wrapping = props => (
  	<Component {...props} otherProp={'addedProp'} />
  )
  return Wrapping;
}

const NewPropToComponent = AddPropHOC(Component);
ReactDOM.render(<NewPropToComponent text={'dummy text'} />, document.getElementById('app'));

Modify property

const ModifyPropHOC = (propName, newValue) => Component => {
	const Wrapping = props => {
  	const modified = Object.assign({}, props);
    modified[propName] = newValue;
  	return <Component {...modified} />;
  }
  Wrapping.propTypes = {
 		text: React.PropTypes.string, 	
  }
  return Wrapping;
}

const ModifyPropInComponent = ModifyPropHOC('text','newValue')(Component);
ReactDOM.render(<ModifyPropInComponent text={'dummy text'} />, document.getElementById('app'));

Remove property

const RemovePropHOC = propName => Component => {
	const Wrapping = props => {
  	let modified = Object.assign({}, props);
  	delete modified[propName];
    return <Component {...modified} />
  }
  return Wrapping;
}
	
const RemovePropInComponent = RemovePropHOC('text')(Component);
ReactDOM.render(<RemovePropInComponent text={'dummy text'} />, document.getElementById('app'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment