Why are State Updater Functions Faster Than Setting State to Plain Values?

76 Views Asked by At

For context, I have a web app that displays an image in a React-Bootstrap Container component (Arena) that holds an image where users are to look and find specific characters.

Separately, I created a div component (CustomCursor) where the background is set to a magnifying glass SVG image.

The Arena component tracks mouse position through an OnMouseMove handler function (handleMouseMove) and passes those coordinates as props to the CustomCursor component.

Here is my Arena component code:

import { useState, useEffect } from 'react';
import { Container, Spinner } from 'react-bootstrap';
import CustomCursor from '../CustomCursor/CustomCursor';
import Choices from '../Choices/Choices';
import { getImageURL } from '../../helpers/storHelpers';

import './Arena.scss';

export default function Arena(props) {

    const [arenaURL, setArenaURL] = useState('');
    const [loaded, setLoaded] = useState(false);
    const [clicked, setClicked] = useState(false);
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);

    function handleClick(e) {
        setClicked(true);
    }

    function handleMouseMove(e) {
        setX(prevState => { return e.clientX });
        setY(prevState => { return e.clientY });
    }

    useEffect(() => {
        retreiveArena();

        // FUNCTION DEFINITIONS 
        async function retreiveArena() {
            const url = await getImageURL('maps', 'the-garden-of-earthly-delights.jpg');
            setArenaURL(url);
            setLoaded(true);
        }
    }, [])

    return (
        <Container as='main' fluid id='arena' className='d-flex flex-grow-1 justify-content-center align-items-center' onClick={handleClick}>
            {!loaded &&
                <Spinner animation="border" variant="danger" />
            }
            {loaded &&
                <img src={arenaURL} alt='The Garden of Earthly Delights triptych' className='arena-image' onMouseMove={handleMouseMove} />
            }
            {clicked &&
                <Choices x={x} y={y} />
            }
            <CustomCursor x={x} y={y} />
        </Container>
    )
}

Here is my CustomCursor code:

import './CustomCursor.scss';

export default function CustomCursor(props) {

    const { x, y } = props;

    return (
        <div className='custom-cursor' style={{ left: `${x - 64}px`, top: `${y + 50}px` }} />
    )
}

When I first created the OnMouseMove handler function I simply set the x and y state values by passing them into their respective state setter functions directly:

    function handleMouseMove(e) {
        setX(e.clientX);
        setY(e.clientY);
    }

However, I noticed this was slow and laggy and when I refactored this function to use setter functions instead it was much faster (what I wanted):

    function handleMouseMove(e) {
        setX(prevState => { return e.clientX });
        setY(prevState => { return e.clientY });
    }

Before:

enter image description here

After:

enter image description here

Why are using setter functions faster than passing in values directly?

1

There are 1 best solutions below

3
On

This is interesting. First of all, we need to focus on reacts way of updating state. In the documentation of react https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous There you can see: React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. For example, this code may fail to update the counter:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

A pretty good article on this is written by Jan Hesters here: https://medium.com/@jan.hesters/updater-functions-in-reacts-setstate-63c7c162b16a

And more details here: https://learn.co/lessons/react-updating-state