makeUseCollection/index.js

import { useCallback } from "react";
import identity from "lodash/identity";
import makeUseMember from "../makeUseMember";

/**
 * @description A hook that handles the CRUD operations on a state's collection.
 * @example
 * const {collection, add, push, edit, remove, error, setError} = useCollection({
 *   fieldKey: 'skills'
 * });
 *
 * push('my'); // collection [my]
 * add(0, 'hello'); // collection[hello, my]
 * add(1, 'world'); // collection[hello, world, my]
 * edit(2, 'sorry'); // collection[hello, world, sorry]
 * remove(2); // collection[hello, world]
 */

const makeWithUniq = (uniq = false) => {
  if (!uniq) return identity;
  return collection => [...new Set(collection)];
};

export default (StateCtx, DispatchCtx) => {
  const useMember = makeUseMember(StateCtx, DispatchCtx);

  return ({ fieldKey, resourceId, uniq, getUpdate, select }) => {
    const [collection, setCollection, error, setError] = useMember({
      fieldKey,
      resourceId,
      getUpdate,
      select
    });
    const normalize = makeWithUniq(uniq);

    const remove = useCallback(
      index => {
        if (index < 0 || index >= collection.length) return;

        setCollection(
          normalize([
            ...collection.slice(0, index),
            ...collection.slice(index + 1)
          ])
        );
      },
      [collection]
    );

    const edit = useCallback(
      (index, value) => {
        if (index < 0 || index >= collection.length) return;

        setCollection(
          normalize([
            ...collection.slice(0, index),
            value,
            ...collection.slice(index + 1)
          ])
        );
      },
      [collection]
    );

    const add = useCallback(
      (index, value) => {
        if (index < 0) return;

        setCollection(
          normalize([
            ...collection.slice(0, index),
            value,
            ...collection.slice(index)
          ])
        );
      },
      [collection]
    );

    const push = useCallback(value => add(collection.length, value), [
      collection
    ]);

    const reorder = useCallback(
      (startIndex, endIndex) => {
        if (startIndex < 0 || startIndex >= collection.length) return;
        if (endIndex < 0 || endIndex >= collection.length) return;

        const mutable = [...collection];
        const [removed] = mutable.splice(startIndex, 1);
        mutable.splice(endIndex, 0, removed);
        setCollection(normalize(mutable));
      },
      [collection]
    );

    return {
      collection,
      set: setCollection,
      add,
      push,
      edit,
      remove,
      reorder,
      error,
      setError
    };
  };
};