DEV Community

Yusub Yacob
Yusub Yacob

Posted on

Best React custom hooks.

Hi Guys, could anyone can share some bast custom hooks ?
thanks.

Top comments (2)

Collapse
 
hassanzohdy profile image
Hasan Zohdy

You can find some pretty good hooks here.

github.com/hassanzohdy/react-hooks

Here are some examples of what you will find there.

useFetcher

This is a powerful fetcher hook that will help you to fetch data from the server and handle the loading state and errors but with more configurations.

import { useFetcher } from '@mongez/react-hooks';
import { getProductsList } from './api';

export default function ProductsList() {
  const {records, isLoading, error, loadMore, goToPage } = useFetcher(params => getProductList(params));

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (error) {
    return <div>{error}</div>;
  }

  return (
    <>
      {records.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
      <button onClick={loadMore}>Load More</button>

      <button onClick={() => goToPage(2)}>Go To Page 2</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can also know which is the current page you're on by using currentPage property.

Another cool feature is paginatable property which tells you whether you should display the pagination buttons or not.

You might also use isLastPage to know if you're on the last page or not so you can disable the load more button.

Here are the entire properties you can use:

type FetcherOutput = {
  records: any[]; // the fetched records
  error: null | any; // the error if any
  load: (params?: Record<string, any>) => Promise<any>; // load the data
  reload: (params?: Record<string, any>) => Promise<any>; // reload the same data
  isLoading: boolean; // whether the data is loading or not
  loadMore: (params?: Record<string, any>) => Promise<any>; // load more data
  goToPage: (page: number) => Promise<any>; // go to a specific page
  reset: () => Promise<any>; // load the default params 
  isFirstPage: boolean; // whether you're on the first page or not
  isLastPage: boolean; // whether you're on the last page or not
  currentPage: number; // the current page you're on
  response?: AxiosResponse;// the response object
  totalPages: number; // the total pages
  totalRecords: number; // the total records
  currentRecords: number; // the current records
  defaultParams: Record<string, any>; // the default params that will be passed to the fetcher method on each request beside the current params
  params: Record<string, any>; // the current params
  paginatable: boolean; // whether the data is paginatable or not
}
Enter fullscreen mode Exit fullscreen mode

Also you can set fetching options by passing it as the second argument to the useFetcher hook.

const {records, isLoading, error, loadMore, goToPage } = useFetcher(params => getProductList(params), {
  defaultParams: {
    page: 1,
    perPage: 10,
  },
});
Enter fullscreen mode Exit fullscreen mode

Or you can set the default params by using setFetchOptions method.

import { setFetchOptions } from '@mongez/react-hooks';

setFetchOptions({
  defaultParams: {
    page: 1,
    perPage: 10,
  }
});
Enter fullscreen mode Exit fullscreen mode

Here is the entire available options

type FetcherOptions = {
  defaultParams?: Record<string, any>; // the default params that will be passed to the fetcher method on each request beside the current params
  // the keys that will be taken from the `response.data` object and will be used as the output
  // it supports dot notation like `paginationInfo.currentPage` or `meta.totalRecords`
  keys?: {
    records?: string;
    itemsPerPage?: string;
    currentPage?: string;
    totalPages?: string;
    totalRecords?: string;
    currentRecords?: string;
    pageNumber?: string;
  };
};
Enter fullscreen mode Exit fullscreen mode

You can see the entire documentation of usage in our article in Dev.to

useInputValue

useInputValue<T>(defaultValue: T): [T: ((newValue): any => void)]

This hook can be very useful when working with form inputs so it will give you an easier state changer callback to update the input value automatically.

The normal way is:

import { useState } from "react";

export function HelloWorld() {
  const [value, setValue] = useState("");

  return (
    <>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        name="username"
        placeholder="username"
      />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

With useInputValue

import { useInputValue } from "@mongez/react-hooks";

export function HelloWorld() {
  const [value, setValue] = useInputValue("");

  return (
    <>
      <input
        value={value}
        onChange={setValue}
        name="username"
        placeholder="username"
      />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

It can take the value from various types,as the onChange sends an event e, the useInputValue valueDetector will try to get the value from any of the following

  • e.target.value
  • e.value
  • e.id
  • e.text
  • e

If no value for e it will be set as empty string.

useOnce

useOnce(callback: () => (void | Destructor))

Works exactly the same as useEffect(() => {}, []) but this one will relive you from adding the second argument and will skip the eslint checker for any missing dependencies that will not reflect on the side effect.

Please note that this hook will ignore any updates that occurs to any states/variables inside it as it will be executed only once after the first render.

useForceUpdate

useForceUpdate(): () => void

A very useful hook when you want to re-render the component without using the useState hook.

import { useForceUpdate } from "@mongez/react-hooks";

export function HelloWorld() {
  const forceUpdate = useForceUpdate();

  useEffect(() => {
    // just dummy example for demonstration
    document.addEventListener("scroll", () => {
      forceUpdate();
    });
  }, []);

  // this value will be updated each time the user scrolls up or down
  const scrollTop = window.pageYOffset;

  return (
    <>
      <div>Scroll Top Position Is: ${scrollTop}</div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

useOuterClick

This hook will allow you to detect when the user clicks outside an element.

Doesn't work with React Native.

import { useOuterClick } from "@mongez/react-hooks";

export function HelloWorld() {
  const divRef = useOuterClick((e, element) => {
    console.log("Clicked outside!");
  });

  return (
    <>
      <div ref={divRef}>This</div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

The e represents the click event and the element represent the value of the divRef's current element.

useStateDetector

useStateDetector(propValue: any): [propValue: any, valueUpdater: (newValue: any) => void]

This hook works exactly the same as useState except it will be updated if the given value is updated.

This one is very useful if you're relying on a value passed from the parent component and needs to detect if it has been changed.

export function ParentComponent() {
  const [disabledValue, setDisabled] = useState(false);

  return (
    <>
      <button onClick={() => setDisabled(!disabledValue)}>
        Toggle Child Button State: ({disabledValue ? "Enabled" : "Disabled"})
      </button>
      <ChildComponent disabled={disabledValue} />
    </>
  );
}

function ChildComponent({ disabled }) {
  const [internalDisabled, setDisabled] = useState(disabled);

  return <>Child Component is: {internalDisabled ? 'Enabled' : 'Disabled'}</>;
}
Enter fullscreen mode Exit fullscreen mode

After the first click on the button the value will remain false, now let's try useStateDetector

import { useStateDetector } from "@mongez/react-hooks";

export function ParentComponent() {
  const [disabledValue, setDisabled] = useState(false);

  return (
    <>
      <button onClick={() => setDisabled(!disabledValue)}>
        Toggle Child Button State: ({disabledValue ? "Enabled" : "Disabled"})
      </button>
      <ChildComponent disabled={disabledValue} />
    </>
  );
}

function ChildComponent({ disabled }) {
  const [internalDisabled, setDisabled] = useStateDetector(disabled);

  return <>Child Component is: {internalDisabled ? 'Enabled' : 'Disabled'}</>;
}
Enter fullscreen mode Exit fullscreen mode

In this case it will always get updated whenever the passed disabled prop in the ChildComponent is updated.

Collapse
 
yusub profile image
Yusub Yacob

thank you for your advice .