Save React state buffer

Let's assume that you want to create an undoable component in React.

For example:

jsx
import React, { useState } from 'react';

const UndoableInput = () => {
    
    const [name, setName] = useState('');
    
    const handleName = (e) => {
        
        setName(e.target.value);
        
    }
    
    const undo = () => {
        
        // Go back to previous state
        
    }
    
    return(
        <>
            <input  onChange = {handleName} value = {name}/>
            <button onClick  = {undo}>Undo</button>
        </>
    );
    
}

export default UndoableInput;

You'll need to save the state on a buffer, and point out the current position using an index variable.

Creating the Hook

To do it, it's convenient to create a new Hook:

jsx
import React, { useEffect, useState } from 'react';

const useStateBuffer = ({initial}) => {
    
    const [buffer, setBuffer] = useState([initial]);
    const [index,  setIndex]  = useState(0);
    const [name, setName]     = useState(initial);
    
    useEffect(() => {
        
        // When name changes, push it to buffer
        let update = [...buffer, name];
        
        // Update buffer and index
        setBuffer(update);
        setIndex(index + 1);
        
    }, [name]);
    
    const undo = () => {
        
        // Go one position back on the buffer
        let pos = Math.max(0, index - 1);
        
        // Gettin subarray until that position included
        let update = buffer.slice(0, pos + 1);
        
        // Update buffer and index
        setBuffer(update);
        setIndex(pos);
    }
    
    return [buffer[index], setName, undo];
    
}

Using the Hook

Finally, you can us the new Hook:

jsx
import React          from 'react';
import useStateBuffer from './useStateBuffer';

const UndoableInput = () => {
    
    const [name, setName, undo] = useStateBuffer('');
    
    return(
        <>
            <input  onChange = {(e) => setName(e.target.value)} value = {name}/>
            <button onClick  = {undo}>Undo</button>
        </>
    );
    
}

export default UndoableInput;

Notice that, if you only destructure the first two variables, useState() and useStateBuffer() behave the same.

Thanks to mtone for guiding me.

Hi, I'm Erik, an engineer from Barcelona. If you like the post or have any comments, say hi.