'Create helper function for a (click handler) function to reuse in multiple React components

For a 'back' button I've created below (onClick) handler function in my React app.

  const { length: historyLength, goBack, replace } = useHistory();

  const handleBack = () => {
    if (historyLength > 2) {
      goBack();
    } else {
      // History length is 2 by default when nothing is pushed to history yet
      // https://stackoverflow.com/questions/9564041/why-history-length-is-2-for-the-first-page
      replace(HomePage);
    }
  };

Then I am passing the onClick handler to my child component like: <Button onClick={handleBack}/>

I am using this handleBack function in multiple places in my React app. Is it a good approach make it e.g. a helper function and how exactly?



Solution 1:[1]

I also don't see any issue with the code or using it as a utility callback.

Is it a good approach make it e.g. a helper function and how exactly?

Anytime you can make your code more DRY (Don't Repeat Yourself) it's generally a good thing. My personal rule-of-thumb is if I've written the same utility code a third time I'll spend a bit of time to refactor it into a common utility (and unit test!!).

I might suggest creating a custom hook to return the back handler.

Example:

import { useHistory } from 'react-router-dom';

const useBackHandler = () => {
  const history = useHistory();

  const handleBack = React.useCallback(() => {
    const { length: historyLength, goBack, replace } = history;

    if (historyLength > 2) {
      goBack();
    } else {
      replace(HomePage);
    }
  }, []);

  return handleBack;
};

export default useBackHandler;

Now you have a single hook to import and use.

import useBackHandler from '../path/to/useBackHandler';

...

const backHandler = useBackHandler();

...

<button type="button" onClick={backHandler}>Back?</button>

If you are needing this function in older class components, then you'll need a way to inject the handleBack as a prop. For this you can create a Higher Order Component.

Example:

import useBackHandler from '../path/to/useBackHandler';

const withBackHandler = Component => props => {
  const backHandler = useBackHandler();
  return <Component {...props} backHandler={backHandler} />;
};

export default withBackHandler;

To use, import withBackHandler and decorate a React component and access props.backHandler.

import withBackHandler from '../path/to/withBackHandler';

class MyComponent extends React.Component {
  ...

  someFunction = () => {
    ...
    this.props.backHandler();
  }

  ...
}

export default withBackHandler(MyComponent);

Solution 2:[2]

@meez

Don't see why this wouldn't work. Just a couple of things: (a) I would add the event argument and e.preventDefault() within the function and (b) would be careful of the function name you are passing on the onClick property of your button: handleBackClick !== handleBack, you'll get an ReferenceError because of an undefined function.

Additionally, I also noticed that this can be achieved with native browser functions. Here's a snippet:

const { length: historyLength, back } = window.history;
const { replace } = window.location;

const handleBack = (e) => {
  e.preventDefault();
  if (historyLength > 2) {
    back();
  } else {
    replace('homepageUrl');
  }
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Hector Sosa