4 custom react hooks that will make your development easier

4 custom react hooks that will make your development easier

1. useToggle

Basically, what this hook does is that, it takes a parameter with value true or false and toggles that value to opposite. It's useful when we want to take some action into it's opposite action.

For example;

  • show and hide modal,
  • show more/show less text,
  • open/close side menu.
// Hook
// Parameter is the boolean, with default "false" value
const useToggle = (initialState = false) => {
    // Initialize the state
    const [state, setState] = useState(initialState);

    // Define and memorize toggler function in case we pass down the comopnent,
    // This function change the boolean value to it's opposite value
    const toggle = useCallback(() => setState(state => !state), []);

    return [state, toggle]
}

// Usage
function App() {
    // Call the hook which returns, current value and the toggler function
    const [isTextChanged, setIsTextChanged] = useToggle();

    return (
      <button onClick={setIsTextChanged}>
                {isTextChanged ? 'Toggled' : 'Click to Toggle'}
            </button>
    );
}

2. useOnClickOutside

This hook allows you to detect clicks outside of a specified element.

By abstracting this logic out into a hook we can easily use it across all of our components that need this kind of functionality.

For Example;

  • dropdown menus,
  • tooltips,
  • modals
  • etc..
// Hook
const useOnClickOutside = (ref, handler) => {
  useEffect(
    () => {
      const listener = (event) => {
        // Do nothing if clicking ref's element or descendent elements
        if (!ref.current || ref.current.contains(event.target)) {
          return;
        }
        handler(event);
      };
      document.addEventListener("mousedown", listener);
      document.addEventListener("touchstart", listener);
      return () => {
        document.removeEventListener("mousedown", listener);
        document.removeEventListener("touchstart", listener);
      };
    },

    [ref, handler]
  );
}
// Usage
function App() {
  // Create a ref that we add to the element for which we want to detect outside clicks
  const ref = useRef();
  // State for our modal
  const [isModalOpen, setModalOpen] = useState(false);
  // Call hook passing in the ref and a function to call on outside click
  useOnClickOutside(ref, () => setModalOpen(false));
  return (
    <div>
      {isModalOpen ? (
        <div ref={ref}>
          👋 Hey, I'm a modal. Click anywhere outside of me to close.
        </div>
      ) : (
        <button onClick={() => setModalOpen(true)}>Open Modal</button>
      )}
    </div>
  );
}

3. useSubmit

Instead of recreating loading and form states we can also move that logic inside a hook called useSubmit

// Hook
const useSubmit = submitFunction => {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

    const handleSubmit = async () => {
    try {
      setLoading(true);
      setError(null);
      await submitFunction();
    } catch (error) {
      setError(error);
    } finally {
      setLoading(false);
    }
  };

  return [handleSubmit, loading, error];
};
// Usage
function App() {
    const mySubmitFunction = () => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
                resolve("Timeout Finished")
        }, 2000);
      });
    };
    const [handleSubmit, loading, error] = useSubmit(mySubmitFunction);

    return (
        <div className="App">
          <button onClick={handleSubmit} disabled={loading}>
            {!loading ? "Click me" : "Loading..."}
          </button>
          {error && <div>{error}</div>}
        </div>
      );
}

4. useMediaQuery

If you want to write css in js or you just want to add some sideEffect to the specific device or breakpoint this hook is for you.

// Hook
const useMediaQuery = (query = "(min-width: 576px)") => {
  const [matches, setMatches] = useState(false);

  useEffect(() => {
    const media = window.matchMedia(query);
    if (media.matches !== matches) {
      setMatches(media.matches);
    }
    const listener = () => {
      setMatches(media.matches);
    };
    media.addListener(listener);
    return () => media.removeListener(listener);
  }, [matches, query]);

  return matches;
}

// Usage
const isTablet = useMediaQuery("(max-width: 1200px)");