DEV Community

Cover image for Save the previous value of a variable
Phuoc Nguyen
Phuoc Nguyen

Posted on • Edited on • Originally published at phuoc.ng

Save the previous value of a variable

In our previous post, we learned about the useRef() hook, which helps us persist values to avoid unnecessary re-renders. Today, we'll delve into another practical application of useRef(): tracking the previous value of a variable.

Tracking the previous value of a variable can be useful in many ways. For instance, it can help us detect changes in state and trigger certain actions accordingly. It can also aid in comparing the current value with the previous value to determine if there have been any changes.

This is especially handy when implementing undo/redo functionality, where you need to compare the current value of something to its previous value. By being able to easily access the previous value, we can compare it with the current value and determine if any changes have been made.

Moreover, tracking the previous value can be helpful when dealing with form inputs or user interactions. By keeping track of the previous value, we can easily revert back to it if needed or validate against it.

Overall, there are numerous scenarios where tracking the previous value of a variable can come in handy, making our code more robust and efficient.

Keeping track of previous values with a custom hook

We'll create a custom hook called usePrevious to keep track of the previous value of a state in React.

This hook uses a ref to store the previous value, which gets updated every time the component re-renders. Here's an example implementation of the usePrevious hook:

import * as React from 'react';

const usePrevious = (value) => {
    const ref = React.useRef();
    React.useEffect(() => {
        ref.current = value;
    });
    return ref.current;
};
Enter fullscreen mode Exit fullscreen mode

In this example, we're creating a new ref with useRef to store the previous value. Then, we're using useEffect to update this ref every time the component re-renders. Finally, we're returning the current value of the ref using return ref.current.

By using this hook in your components, you can easily access and compare previous values without the hassle of manually storing them yourself. This can greatly simplify your code and make it easier to understand.

Discovering scroll direction

Let's explore some useful applications of the usePrevious hook. First, we'll discuss detecting the scroll direction.

Detecting scroll direction is commonly used for infinite scrolling. By knowing whether the user is scrolling up or down, we can fetch data accordingly and append it to the existing list. This boosts performance by reducing the amount of data that needs to be loaded at once.

Another use case is for scroll-based animations. You may want to trigger an animation when a certain element comes into view while scrolling down, but not when scrolling up. By detecting the scroll direction and position, you can easily determine when to trigger the animation.

Additionally, detecting scroll direction can be useful in creating custom navigation menus that change based on the user's scroll position. For example, you could have a menu that appears at the top of the page when scrolling up and disappears when scrolling down, providing a more immersive user experience.

Overall, detecting scroll direction can be creatively used in many ways to enhance your web applications.

To track changes in the scroll position, we can use the usePrevious hook. This hook helps us keep track of the previous value of the scroll position, so we can determine whether it has increased or decreased. To get started, we need to create a state variable that will hold our current scroll position.

const [scrollPosition, setScrollPosition] = React.useState(0);
Enter fullscreen mode Exit fullscreen mode

To figure out where the user is currently scrolling, we need to handle the scroll event. In our example, we create a function called handleScroll that updates the scrollPosition state variable with the current value of window.scrollY.

const handleScroll = () => setScrollPosition(window.scrollY);
Enter fullscreen mode Exit fullscreen mode

Next, we use the useEffect hook to add an event listener for the scroll event. This listener calls our handleScroll function each time the event occurs. We also remove the listener when our component unmounts to avoid any memory leaks.

React.useEffect(() => {
    document.addEventListener("scroll", handleScroll);
    return () => {
        document.removeEventListener("scroll", handleScroll);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

Good practice

To improve the performance of your scroll handler, it's recommended to use either debounce or throttle. This is because the scroll event can fire multiple times per second, which can slow down your website or app if your handler function is doing a lot of work.

Debouncing and throttling are techniques that limit the number of times a function is called based on a certain time
interval. Debouncing waits until a certain amount of time has passed since the last time the function was called before calling it again. Throttling, on the other hand, only calls the function once every set interval. These techniques can help ensure that your scroll handler is efficient and doesn't negatively impact the user experience.

In our example, we'll use the throttle function from the lodash library to control the frequency of our handleScroll function.

import { throttle } from 'lodash';

const handleScrollThrottled = throttle(handleScroll, 100);

React.useEffect(() => {
    document.addEventListener("scroll", handleScrollThrottled);
    return () => {
        document.removeEventListener("scroll", handleScrollThrottled);
    };
}, []);

In this example, we import the throttle function from lodash to optimize our scroll event handling. We create a new function, handleScrollThrottled, by passing in our original handleScroll function and an interval of 100 milliseconds. Then, we add an event listener for the scroll event using our new throttled function. Finally, we remove the listener when our component unmounts.

By using throttling or debouncing in your scroll handlers, you can significantly improve your component's performance, reducing unnecessary re-renders and making your user experience smoother.

Next, we can use the usePrevious hook to retrieve the previous value of our scroll position.

const previousScrollPosition = usePrevious(scrollPosition);
Enter fullscreen mode Exit fullscreen mode

Now that we've stored both our current and previous values as variables, we can compare them in an effect hook that runs every time the current value changes.

React.useEffect(() => {
    if (previousScrollPosition < scrollPosition) {
        console.log("Scroll down");
    } else if (previousScrollPosition > scrollPosition) {
        console.log("Scroll up");
    }
}, [scrollPosition, previousScrollPosition]);
Enter fullscreen mode Exit fullscreen mode

In this example, we log "Scroll down" when the current scroll position is greater than the previous one, and "Scroll up" when it's less. By using this technique, we can quickly detect the scrolling direction and perform actions accordingly.


If you found this series helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

Top comments (0)