makeUseMember/index.js

import { useCallback } from "react";
import useContextSelector from "../useContextSelector";
import identity from "lodash/identity";
import { makeByIdSelector } from "../collection/makeUseSelector";

/**
 * @description A hook that handles the mutate operations on a state's member.
 * @example
 * const [title, setTitle, error, setError] = useMember({
 *   fieldKey: 'title'
 * });
 *
 * setTitle('hello'); // state.title === 'hello'
 * setError('myError'); // state.errors.title === 'myError'
 */
const defaultGetUpdate = resourceId => state =>
  resourceId ? state.updateResource : state.update;

export default (StateCtx, DispatchCtx) => ({
  fieldKey,
  resourceId,
  getUpdate = defaultGetUpdate,
  select = resourceId ? makeByIdSelector : identity
}) => {
  const selector = useCallback(
    state => {
      const member = resourceId ? select(resourceId)(state) : select(state);
      return [member[fieldKey], member.errors];
    },
    [resourceId, fieldKey]
  );
  const [value, errors] = useContextSelector(StateCtx, selector);

  const updateSelector = useCallback(
    state => {
      const update = getUpdate(resourceId)(state);

      if (typeof update !== "function")
        throw new Error("useMember expects an 'update' dispatch function");

      return resourceId ? (key, v) => update(resourceId, key, v) : update;
    },
    [resourceId]
  );
  const update = useContextSelector(DispatchCtx, updateSelector);

  const setValue = useCallback(v => update(fieldKey, v), [update, fieldKey]);

  const error = errors[fieldKey];
  const setError = useCallback(
    e => update("errors", { ...errors, [fieldKey]: e }),
    [update, errors]
  );

  return [value, setValue, error, setError];
};