formik-persist icon indicating copy to clipboard operation
formik-persist copied to clipboard

hook api?

Open sibelius opened this issue 5 years ago • 3 comments

would make sense to transform this in a hook api?

sibelius avatar May 08 '19 19:05 sibelius

This works

import * as React from 'react';
import { FormikProps, FormikState } from 'formik';
import debounce from 'lodash.debounce';
import isEqual from 'react-fast-compare';

// Copied from https://usehooks.com/usePrevious/
// License is Unlicensed, https://github.com/gragland/usehooks/blob/master/LICENSE
function usePrevious<T>(value: T): T | undefined {
  // The ref object is a generic container whose current property is mutable ...
  // ... and can hold any value, similar to an instance property on a class
  const ref = React.useRef<T>();

  // Store current value in ref
  React.useEffect(() => {
    ref.current = value;
  }, [value]); // Only re-run if value changes

  // Return previous value (happens before update in useEffect above)
  return ref.current;
}

export function usePersist(
  formik: FormikProps<any>,
  name: string,
  debounceDelay = 300,
  isSessionStorage = false
) {
  const saveForm = React.useMemo(
    () =>
      debounce((data: FormikState<{}>) => {
        if (isSessionStorage) {
          window.sessionStorage.setItem(name, JSON.stringify(data));
        } else {
          window.localStorage.setItem(name, JSON.stringify(data));
        }
      }, debounceDelay),
    [debounceDelay, isSessionStorage, name]
  );

  React.useEffect(() => {
    const maybeState = isSessionStorage
      ? window.sessionStorage.getItem(name)
      : window.localStorage.getItem(name);
    if (maybeState) {
      formik.setValues(JSON.parse(maybeState));
      console.log("set state", maybeState);
    }
  }, []); // deps must be empty, effect will run only on mount

  const previousValues = usePrevious(formik.values);
  React.useEffect(() => {
    if (previousValues && !isEqual(previousValues, formik.values)) {
      saveForm(formik.values);
    }
  }, [formik.values, previousValues, saveForm]);
}

MichalKalita avatar Feb 27 '20 11:02 MichalKalita

@MichalKalita Its nice solution. However your mount effect hook needs to pass the isSessionStorage and name to the dependency list as this comes from the external caller method. If this changed from session to local storage or vice versa, then the whole context get loaded from the wrapper again.

alonfai avatar Jun 18 '20 02:06 alonfai

How about this one https://github.com/suhaotian/use-formik-persist

suhaotian avatar Jun 19 '21 13:06 suhaotian