Skip to content

Instantly share code, notes, and snippets.

@tokland
Last active May 20, 2018 09:21
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 tokland/825e25546dffd31f2e223e18b6d78e69 to your computer and use it in GitHub Desktop.
Save tokland/825e25546dffd31f2e223e18b6d78e69 to your computer and use it in GitHub Desktop.
Avoid setState in stateful React components
/*
React components are quasi-functional objects. The least functional element of the pack
is probably `setState`, a method that works only with side-effects.
This simple `action` decorator allows you to write pure functions in state transition method. It
works with normal values and promises.
Setup:
$ yarn add --dev babel-plugin-transform-decorators-legacy
$ echo '{"presets": ["es2015", "react"], "plugins": ["transform-decorators-legacy"]}' > .babelrc:
*/
function isPromise(obj) {
return !!obj && typeof obj.then === 'function';
}
export function action(target, name, descriptor) {
const method = descriptor.value;
descriptor.value = function (...args) {
const result = method.apply(this, args);
if (isPromise(result)) {
return result.then(newState => this.setState(newState));
} else {
return this.setState(result);
}
}
}
/* Simple counter example. Decorate any method that returns new state, including
lifecycle component methods. */
import React from 'react';
import { action } from './action';
class Counter extends React.PureComponent {
constructor(props) {
super(props);
this.state = { value : 0 };
}
@action
componentDidMount() {
return { value: 1 };
}
@action
increment() {
const newValue = this.state.value + 1;
return { value: newValue };
}
@action
async asyncIncrement() {
const newValue = await Promise.resolve(this.state.value + 1);
return { value: newValue };
}
render() {
return (
<div>
<p>Value: {this.state.value}</p>
<button onClick={this.increment.bind(this)}>Increment</button>
<button onClick={this.asyncIncrement.bind(this)}>Increment (async)</button>
</div>
);
}
}
export default Counter;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment