Skip to content

Instantly share code, notes, and snippets.

@andrewzey
Last active April 11, 2017 20:40
Show Gist options
  • Save andrewzey/741ecde332f144a14e03074d70611cec to your computer and use it in GitHub Desktop.
Save andrewzey/741ecde332f144a14e03074d70611cec to your computer and use it in GitHub Desktop.
Reusable "Methods" for Functional React Components
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 =).
@andrewzey
Copy link
Author

andrewzey commented Apr 11, 2017

Hmm, another possibility that resolves the downside with Solution 1 (lack of ability to mock functions for tests):

// Solution 4:
// Use closure-scope reference to object of functions defined outside of the functional
// component.
// Pro: save unnecessary re-definition, can stub the "funcs" object for unit tests
// Con: it's weird

const handleClick = event => {
  event.preventDefault();
  console.log('The link was clicked');
};

export const funcs = { handleClick };

const FuncComponent = () => <a onClick={(event) => funcs.handleClick(event)}></a>;

export default FuncComponent;

@andrewzey
Copy link
Author

andrewzey commented Apr 11, 2017

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