Michael-Gibbons / OSB

An Opinionated Full Stack Shopify App Boiler
MIT License
19 stars 2 forks source link

add useNonPrimitiveEffect hook #111

Open Michael-Gibbons opened 1 year ago

Michael-Gibbons commented 1 year ago
import { useEffect } from "react"

const DEFAULT_OPTIONS = {
  maxDepth: 10
}

export const useNonPrimitiveEffect = (callback, inputDeps, options = DEFAULT_OPTIONS) => {
  const depthOf = function(object) {
    var level = 1;
    for(var key in object) {
        if (!object.hasOwnProperty(key)) continue;

        if(typeof object[key] == 'object'){
            var depth = depthOf(object[key]) + 1;
            level = Math.max(depth, level);
        }
    }
    return level;
  }

  if(depthOf(inputDeps) > options.maxDepth){
    throw new Error("Error in useNonPrimitiveEffect, max depth exceeded. This is either an infinite loop or you may need to increase the max depth in useNonPrimitiveEffect.")
  }

  if(!Array.isArray(inputDeps)){
    throw new Error("Error in useNonPrimitiveEffect, dependencies must be an array. Exactly the same as React's useEffect.")
  }

  const reducer = (values, propValue) => {
    if (Array.isArray(propValue)) {
      values = unwrapArray(propValue).reduce(reducer, values);
    } else if (typeof propValue === 'object' && propValue !== null) {
      values = Object.values(propValue).reduce(reducer, values);
    } else {
      values.push(propValue);
    }
    return values;
  };

  const unwrapArray = (values) => {
    let unwrapped = [];
    values.forEach((value) => {
      if (Array.isArray(value)) {
        unwrapped = unwrapped.concat(unwrapArray(value));
      } else {
        reducer(unwrapped, value);
      }
    });
    return unwrapped;
  };

  let watchDeps;
  if (Array.isArray(inputDeps)) {
    watchDeps = unwrapArray(inputDeps).reduce(reducer, []);
  } else {
    watchDeps = Object.values(inputDeps).reduce(reducer, []);
  }

  useEffect(() => {
    callback();
  }, [watchDeps]);
}