Last active
April 11, 2017 20:40
-
-
Save andrewzey/741ecde332f144a14e03074d70611cec to your computer and use it in GitHub Desktop.
Reusable "Methods" for Functional React Components
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
// Class Components | |
export default class ClassComponent extends Component { | |
handleClick(event) { | |
event.preventDefault(); | |
console.log('The link was clicked'); | |
} | |
render() { | |
return <a onClick={(event) => this.handleClick(event)}></a> | |
} | |
} | |
// Functional Components | |
const FuncComponent = () => { | |
// PROBLEM: Gets redefined every time the component is rendered! | |
const handleClick = event => { | |
event.preventDefault(); | |
console.log('The link was clicked'); | |
}; | |
return <a onClick={(event) => handleClick(event)}></a> | |
}; | |
export default FuncComponent; | |
// Solution 1: | |
// Use closure-scope reference to function defined outside of the functional | |
// component. | |
// Pro: save unnecessary re-definition | |
// Con: hard to write unit test, since you don't have access to | |
// the closure scope variable pointing to the function, so it can't be | |
// stubbed | |
const handleClick = event => { | |
event.preventDefault(); | |
console.log('The link was clicked'); | |
}; | |
const FuncComponent = () => <a onClick={(event) => handleClick(event)}></a>; | |
export default FuncComponent; | |
// Solution 2: | |
// Add the function to the prototype of the Functional Component | |
// Pro: save unnecessary re-definition, preserve ability to mock | |
// Con: very hacky, can be hard to understand for new developer, and may be | |
// tricky to debug issues that may occur with React, since it goes against | |
// the recommended React paradigm | |
const handleClick = event => { | |
event.preventDefault(); | |
console.log('The link was clicked'); | |
}; | |
const FuncComponent = () => <a onClick={(event) => this.handleClick(event)}></a>; | |
FuncComponent.prototype.handleClick = handleClick; | |
export default FuncComponent; | |
// Solution 3: | |
// No solution, since the medicine may be worse than the cure =). |
Ok, I think solution 5 is actually the best. It makes testing super easy, and keep the code "React-like" and understandable to a new developer looking at it who knows React patterns:
// Solution 5:
// Use defaultProps to avoid function redefinition
// Pro: save unnecessary re-definition, easy testing (you can just override the
// default prop by passing in a mock function)
// Con: extraneous proptypes
const propTypes = {
handleClick: PropTypes.func
};
const defaultProps = {
handleClick: event => {
event.preventDefault();
console.log('The link was clicked');
};
};
const FuncComponent = ({ handleClick }) => <a onClick={(event) => handleClick(event)}></a>;
FuncComponent.propTypes = propTypes;
FuncComponent.defaultProps = defaultProps;
export default FuncComponent;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hmm, another possibility that resolves the downside with Solution 1 (lack of ability to mock functions for tests):