BradCypert.com
Autosaving with React Hooks
December 21, 2019

Autosaving with React Hooks

Posted on December 21, 2019  (Last modified on March 30, 2023 )
3 minutes  • 539 words
This project uses these versions of languages, frameworks, and libraries.
  • react react : 17
  • typescript typescript : 3.4
This tutorial may work with newer versions and possibly older versions, but has only been tested on the versions mentioned above.

React hooks have really changed the game for me when it comes to building react components. However, I’ve found that writing autosaving functionality is a little less obvious via hooks. Thankfully, it is still possible (and arguably a lot cleaner) when using hooks.

We can accomplish autosaving functionality through use of useEffect. You’ll also need a way to post the data to your server. In my case, I’m using Apollo’s useMutation hook as well. This allows me to post a graphql mutation from a hook-like interface.

The useEffect hook

The useEffect hook effectively replaces componentDidMount, componentWillUpdate, and componentWillUnmount. Here’s how I remember the API for useEffect:

useEffect(() => {
  doWhateverIsHereOnMountandUpdate();

  return () => {
    doWhateverIsHereOnWillUnmount();
  };
}, [skipUntilThisStateOrPropHaschanged]);
useEffect(() => {
  doWhateverIsHereOnMountandUpdate();

  return () => {
    doWhateverIsHereOnWillUnmount();
  };
}, [skipUntilThisStateOrPropHaschanged]);

It’s worth mentioning that the skipUntilThisStateOrPropHasChanged is optional, and leaving it out will cause it to process the hook on every render.

Implementing Autosave

Now that we understand our hook, the autosave functionality becomes fairly trivial. We’ll use a couple of state hooks as well to help us manage the text that a user types in as well as the last value we saved (we can skip network requests if they’re the same).

const [lastText, setLastText] = React.useState<String>("");
const [text, setText] = React.useState<String>("");
const [lastText, setLastText] = React.useState("");
const [text, setText] = React.useState("");

You’ll see how we use lastText in our useEffect hook below, but for now, you just need to know that text represents the state of what the user has typed in. If you’d like more information on how this works, React’s documentation for Controlled Components is a great place to start .

Now, we need a function to trigger to persist our data to the server. In my case, I’ll use an Apollo mutation since the server API processes graphql.

const [updateContent] = useMutation(UPDATE_CHAPTER_CONTENT.MUTATION);
const [updateContent] = useMutation(UPDATE_CHAPTER_CONTENT.MUTATION);

Finally, we can write our useEffect hook:

const AUTOSAVE_INTERVAL: number = 3000;
React.useEffect(() => {
  const timer = setTimeout(() => {
    if (lastText != text) {
      updateContent({ variables: { content: text, id: chapterId } });
      setLastText(text);
    }
  }, AUTOSAVE_INTERVAL);
  return () => clearTimeout(timer);
}, [text]);
const AUTOSAVE_INTERVAL = 3000;
React.useEffect(() => {
  const timer = setTimeout(() => {
    if (lastText != text) {
      updateContent({ variables: { content: text, id: chapterId } });
      setLastText(text);
    }
  }, AUTOSAVE_INTERVAL);
  return () => clearTimeout(timer);
}, [text]);

We’re doing a couple of neat things here. First, we’re setting up our useEffect hook. It creates a timer via setTimeout, and when that hook unmounts, it removes that timer. That’s the “meat-and-potatoes” behind it. You’ll notice that our setTimeout function checks to see if the text has changed before posting our data, and then sets the last text if it has changed. We’re also only triggering this useEffect when text has changed (as indicated by [text] as the second parameter. We probably could clean this up a bit by removing the if in the timeout function body, and updating the useEffect dependency array to be [text != lastText].

And that should do it! Hopefully, this helps if you’re trying to build autosave functionality into React project. If you’d like to learn more about React, you can find my other post on Facebook’s awesome framework here.

Cartoon headshot of Brad Cypert
Follow me

Connect with me to follow along on my journey in my career, open source, and mentorship. Occasionally, I'll share good advice and content (quality not guaranteed).