Search code examples
javascriptreactjsreact-statereusability

How to abstract repetitive React code pattern into a reusable component or object?


Don't spend too much time analyzing this section, just pay attention to the similarities of each group.

// LOADING ALL VENDORS
const [loadingAllVendors, setLoadingAllVendors] = useState<boolean>(true);
const loadAllVendorsErrorTimeout = useRef<NodeJS.Timeout|null>(null);
const [loadAllVendorsError, setLoadAllVendorsError] = useState<string|null>(null);
const handleLoadAllVendorsError = (error: string|null) => {
  if (error) console.error(error);
  setLoadAllVendorsError(error);
};
const loadAllVendorsErrorTime: number = 6;
const timedLoadAllVendorsError = useCallback((error: string, seconds: number) => {
  handleLoadAllVendorsError(error);
  if (loadAllVendorsErrorTimeout.current) clearTimeout(loadAllVendorsErrorTimeout.current);
  loadAllVendorsErrorTimeout.current = setTimeout(() => {
    setLoadAllVendorsError(null);
  }, seconds * 1000)
}, []);

// LOADING ALL MANUFACTURERS
const [loadingAllManufacturers, setLoadingAllManufacturers] = useState<boolean>(true);
const loadAllManufacturersErrorTimeout = useRef<NodeJS.Timeout|null>(null);
const [loadAllManufacturersError, setLoadAllManufacturersError] = useState<string|null>(null);
const handleLoadAllManufacturersError = (error: string|null) => {
  if (error) console.error(error);
  setLoadAllManufacturersError(error);
};
const loadAllManufacturersErrorTime: number = 6;
const timedLoadAllManufacturersError = useCallback((error: string, seconds: number) => {
  handleLoadAllManufacturersError(error);
  if (loadAllManufacturersErrorTimeout.current) clearTimeout(loadAllManufacturersErrorTimeout.current);
  loadAllManufacturersErrorTimeout.current = setTimeout(() => {
    setLoadAllManufacturersError(null);
  }, seconds * 1000);
}, []);
  
// SEARCHING PARTS
const [searching, setSearching] = useState<boolean>(false);
const searchErrorTimeout = useRef<NodeJS.Timeout|null>(null);
const [searchError, setSearchError] = useState<string|null>(null);
const handleSearchError = (error: string|null) => {
  if (error) console.error(error);
  setSearchError(error);
};
const searchErrorTime: number = 6;
const timedSearchError = useCallback((error: string, seconds: number) => {
  handleSearchError(error);
  if (searchErrorTimeout.current) clearTimeout(searchErrorTimeout.current);
  searchErrorTimeout.current = setTimeout(() => {
    setSearchError(null);
  }, seconds * 1000);
}, []);

If you'll notice, each of these blocks of code share a pattern of six things:

  1. A useState boolean value, named something along the lines of 'loadingSomething'
  2. A useRef NodeJS.Timeout or null, named something like 'loadSomethingErrorTimeout'
  3. A useState string or null value, named something like 'loadSomethingError'
  4. A function taking a string or null as params, named something like 'handleSomethingError'
  5. A number, named something like 'somethingErrorTime'
  6. A function taking a string and number as params, named something like 'timedSomethingError'

My goal here is to create a reuseable component/object from this pattern and remove all the 'something's from the names internally, but I'm not sure if components can be used like this. I feel like there is a way to achieve my goal, I just don't know what it is.


Solution

  • Custom hooks solve this issue.