Skip to content

Instantly share code, notes, and snippets.

@moredip
Last active July 3, 2017 06:15
Show Gist options
  • Save moredip/4b8053ba60e554ecda86b612715c8ddd to your computer and use it in GitHub Desktop.
Save moredip/4b8053ba60e554ecda86b612715c8ddd to your computer and use it in GitHub Desktop.
functionally-stateful

stateful()

A helper function that lets us write stateful React components in the same functional style as stateless components.

Why?

  • sidesteps the need to introduce class and this-binding confusion into your codebase.
  • slightly terser, slightly more declarative
  • opinionated - doesn't expose the more dangerous features of class-based Components (e.g. lifecycle callbacks)

When you have a team working on a larger React codebase it is hard for everyone to understand which patterns are preferable vs. those which are a necessary compromise.

In my opinion stateful components are sometimes neccessary in a React app but there aren't many other good reasons to implement a component using the class-based approach. This helper allows us to implement stateful components without having to introduce a class-based component. This helps to make a class-based component an odd case which hopefully stands out for further discussion.

Additionally, this helper removes the need for boilerplate this-binding of handler functions. It's distracting, hard for folks to learn, and very easy to miss.

export class ComposeMessage extends Component {
constructor(props){
super(props);
this.state = {
message: ''
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render(){
return (
<form className="compose-message" onSubmit={this.handleSubmit}>
<input
type="text"
className="compose-message__input"
value={this.state.message}
onChange={this.handleInputChange}
placeholder="write a message"
/>
<input
type="submit"
className="compose-message__submit"
value="Send"
/>
</form>
);
}
handleInputChange(event){
const message = event.target.value;
this.setState({message});
}
handleSubmit(event){
event.preventDefault();
if( this.props.onMessage ){
this.props.onMessage(this.state.message);
}
this.setState({
message:''
});
}
}
export const ComposeMessage = stateful(
{
initialState: { message: '' }
},
function ComposeMessage({props,state,setState}) {
return (
<form className="compose-message" onSubmit={handleSubmit}>
<input
type="text"
className="compose-message__input"
value={state.message}
onChange={handleInputChange}
placeholder="write a message"
/>
<input
type="submit"
className="compose-message__submit"
value="Send"
/>
</form>
);
function handleInputChange(event){
const message = event.target.value;
setState({message});
}
function handleSubmit(event){
event.preventDefault();
if( props.onMessage ){
props.onMessage(state.message);
}
setState({
message:''
});
}
}
);
import {Component} from 'react';
export default function stateful({initialState},statelessRenderFn){
let comp = class extends Component {
constructor(props){
super(props);
this.state = initialState;
}
render(){
return statelessRenderFn({
props: this.props,
state: this.state,
setState: this.setState.bind(this)
});
}
};
comp.displayName = statelessRenderFn.name;
return comp;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment