Let's assume that you want to create an undoable component in React.
For example:
jsximport 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.
To do it, it's convenient to create a new Hook:
jsximport 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]; }
Finally, you can us the new Hook:
jsximport 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.