Make writing a habit together! This is the second day of my participation in the “Gold Digging Day New Plan · April More text challenge”. Click here for more details.

React state management: React state management: React state management: React state management: React state management: React state management: React state management

There is a common requirement: it is necessary to open the popbox to select a data item on the page, which involves a data synchronization problem. The page is the original data, and the popbox is the edited data.

1. When opening the popbox, the original data of the page will be synchronized to the popbox — >> When the user clicks OK, the new data needs to be synchronized to the original data;

2. When the user clicks Cancel — >> the edited data needs to be restored to the original data. So we’ll write a forward method and a BACKWARD method, and here’s the pseudocode.

const [initialData, setInitialData] = useState('naruto');
const [currentData, setCurrentData] = useState(initialData);

const forward = () = > {
	setInitialData(currentData);
}

const backward = () = > {
	setCurrentData(initialData);
}

return (
	<>
		<button onClick={backward}>cancel</button>
		<button onClick={forward}>determine</button>
	</>
)
Copy the code

It’s a relatively simple interaction, with only two points in time synchronizing with each other, but what if it’s more complicated?A complex editable table with back and forward functions, at which point we need to record each node of state change. It is best to abstract this function into a hook, as convenient as using useState.

Just small make up recently depth depends on ahooks (ahooks just released version 3.0, added a lot of practical hook), which provides a hook is called useHistoryTravel, see the API that fully conform to the requirements.

import { useHistoryTravel } from 'ahooks';
import React from 'react';
 
export default() = > {const { value, setValue, backLength, forwardLength, back, forward } = useHistoryTravel<string>();
 
  return (
    <div>
      <input value={value || ''} onChange={(e)= > setValue(e.target.value)} />
      <button disabled={backLength< =0} onClick={back} style={{ margin: '0 8px' }}>
        back
      </button>
      <button disabled={forwardLength< =0} onClick={forward}>
        forward
      </button>
    </div>
  );
};
Copy the code

Back and forward methods can be used to move the state back or forward during initialization. Let’s have a look at its source code:

const [history, setHistory] = useState<IData<T | undefined> > ({present: initialValue,
  past: [].future: [],});Copy the code

It internally defines a state called history, which contains an array of current states, past states, and future states. Thus it can be seen that each change in the state is recorded, and then the state is randomly traversed by backward and forward methods.

const _forward = (step: number = 1) = > {
   if (future.length === 0) {
     return;
   }
   const { _before, _current, _after } = split(step, future);
   setHistory({
     past: [...past, present, ..._before],
     present: _current,
     future: _after,
   });
 };
 
 const _backward = (step: number = -1) = > {
   if (past.length === 0) {
     return;
   }
 
   const { _before, _current, _after } = split(step, past);
   setHistory({
     past: _before,
     present: _current,
     future: [..._after, present, ...future],
   });
 };
Copy the code

After browsing the source code, I found that this seemingly complex function can be so simple to implement.

Although the function is implemented, it still feels like something is missing. It would be better to be able to create a snapshot of status just like photoshop to create a snapshot of history.

In fact, there is a need for such a function, when you want to save after editing, in fact, it is a snapshot function, when you want to cancel the next time after editing can be restored to the latest snapshot state.

So if we look at the useHistoryTravel API, the reset method can pass in a new state and override the original state defined at initialization, and then the next time we call the reset method,

The state will revert to the original state when it was last overwritten.

const reset = (. params: any[]) = > {
  const _initial = params.length > 0 ? params[0] : initialValueRef.current;
  initialValueRef.current = _initial;
 
  setHistory({
    present: _initial,
    future: [].past: [],}); };Copy the code

A simple snapshot function can be implemented, but from the code to see, each reset, past and future are cleared, do not let back after saving? This is obviously not logical, so xiaobian uses a simple and effective method,

const { value: dataSource, setValue: setDataSource } = useHistoryTravel();
const { value: snapshot, setValue: setSnapshot} = useHistoryTravel();
Copy the code

Just define a separate state to manage snapshots, haha.

Of course, if you want to be able to achieve a more perfect, of course, it is directly on the source base of the extension, with small insight, can change the state definition to the following structure:

interface IValue<T> { value? : T; id: string; timestamp: number; The snapshot: Boolean; } interface IData<T> { current? : T; present: IValue<T>; past: IValue<T>[]; future: IValue<T>[]; }Copy the code

The state value is stored in an object, which is initialized or updated with a unique ID, timestamp, snapshot status, etc. These are very useful information, which makes it more flexible to use.