0

I'm using React Hook Form v7 and I'm trying to make my data form persistent on page reload. I read the official RHF documentation which suggests to use little state machine and I tried to implement it but without success. Is there a better way to do it? However...

The first problem I encountered using it, is that my data is a complex object so the updateAction it should be not that easy.

The second problem is that I don't know when and how to trigger the updateAction to save the data. Should I trigger it on input blur? On input change?

Here's my test code:

Edit busy-rgb-ljbo55

3
  • By page reload do you mean if the user refreshes the whole app ?
    – Hugo Bp
    Mar 19 at 16:57
  • @HugoBp What do you mean by “the whole app”? I mean, if the user clicks on browser refresh button the data is persistent.
    – KaMZaTa
    Mar 19 at 20:31
  • Ok, I was indeed asking whether you were talking about persisting data on page change within your app because that's what the RHF documentation you linked talks about. Now I understand that's not what you are after.
    – Hugo Bp
    Mar 19 at 22:59

2 Answers 2

0

The state itself won't persist any data on page reload.
You need to add your state data to Local Storage.
Then load it back into the state on componentDidMount (useEffect with empty dependency array).

const Form = () => {
  const [formData, setFormData] = useState({})
  
  useEffect(() => {
    if(localStorage) {
      const formDataFromLocalStorage = localStorage.getItem('formData');
      if(formDataFromLocalStorage) {
        const formDataCopy = JSON.parse(formDataFromLocalStorage)
        setFormData({...formDataCopy})
      }
    }
  }, []);
  
  useEffect(() => {
    localStorage && localStorage.setItem("formData", JSON.stringify(formData))
  }, [formData]);
  
  const handleInputsChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value
    })
  }
  
  return (
    <div>
      <input 
        type="text" 
        name="firstName"
        placeholder='first name'
        onChange={e => handleInputsChange(e)}
        value={formData?.firstName}
      />
      <input 
        type="text" 
        name="lastName"
        placeholder='last name'
        onChange={e => handleInputsChange(e)}
        value={formData?.lastName}
      />
    </div>
  )
}

Edit gracious-hoover-oehb0w

2
  • Thanks for your answer but it doesn't work for my example (please, check my CodeSandbox). I'm using RHF and my formData is a complex object.
    – KaMZaTa
    Mar 20 at 14:59
  • I mean, using useEffect to set localStorage I think it could be a good idea the use little-state-machine but how can I update the whole RHF state object? I could watch every change using methods.watch() but then I should update the RHF state with the new one. How?
    – KaMZaTa
    Mar 20 at 19:47
0

If persisting in the localStorage works you, here is how I achieved it.

Define a custom hook to for persisting the data

export const usePersistForm = ({
  value,
  localStorageKey,
}) => {
  useEffect(() => {
    localStorage.setItem(localStorageKey, JSON.stringify(value));
  }, [value, localStorageKey]);

  return;
};

Just use it in the form component

const FORM_DATA_KEY = "app_form_local_data";

export const AppForm = ({
  initialValues,
  handleFormSubmit,
}) => {
  // useCallback may not be needed, you can use a function
  // This was to improve performance since i was using modals
  const getSavedData = useCallback(() => {
    let data = localStorage.getItem(FORM_DATA_KEY);
    if (data) {
     // Parse it to a javaScript object
      try {
        data = JSON.parse(data);
      } catch (err) {
        console.log(err);
      }
      return data;
    }
    return initialValues;
  }, [initialValues]);

  const {
    handleSubmit,
    register,
    getValues,
    formState: { errors },
  } = useForm({ defaultValues: getSavedData() });
  const onSubmit: SubmitHandler = (data) => {
     // Clear from localStorage on submit
     // if this doesn’t work for you, you can use setTimeout
     // Better still you can clear on successful submission
    localStorage.removeItem(FORM_DATA_KEY);
    handleFormSubmit(data);
  };

  // getValues periodically retrieves the form data
  usePersistForm({ value: getValues(), localStorageKey: FORM_DATA_KEY });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      ...
    </form>
   )
}

3
  • Could you please edit my CodeSandbox example and show me how to do it in that specific case? I tried but it seems not work with my form example. I receive a lot of Uncaught TypeError: Cannot destructure property 'zip' of 'watch(...)' as it is undefined.. As I wrote, I'm dealing with a complex state object.
    – KaMZaTa
    yesterday
  • I have edited the CodeSandbox, try it here codesandbox.io/embed/… yesterday
  • I have edited the post it to fix the error, you have to parse object strings to javaScript object else it will throw an error. yesterday

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.