Custom hook simply implements React state management

background

When we develop front-end requirements, we will encounter the problem of synchronizing the internal state of multiple components, and these components are often far apart in the DOM structure. Take the common "skinning function" in web pages as an example. WhenThemeSwitch a component changes the internal theme color state , it hopes to update the theme color of other components in real time, so as to realize the real-time preview function of dynamic theme switching:color

In order to achieve similar functionality, you may use a state management library such as Redux. But there are two questions worth thinking about:

1. I just want to synchronize a state, can it be easily achieved without introducing a third-party library?
2. How is the internal implementation of the state management class library, and what is the principle?

problem analysis

There are many ways to analyze the problem, which can be roughly divided into two types:

  • 0 - 1: Run the code first, encounter problems step by step, and solve the problem; it is suitable for problems with a clear problem-solving path;
  • 1 - 0: Starting from the result, analyze the preconditions to achieve the result, trace back to the source; suitable for dismantling complex problems;

Here we use the 1 - 0 method to analyze:

1. Notification synchronization

To achieve state synchronization, when the theme switching component ThemeSwitchchanges the internal theme state and executes setColor(newColor), we only need to notify other components to perform the same setColor(newColor)operation to update their state, thus achieving the purpose of state synchronization:

2. How to notify?

Notification is actually the execution of the setColor method inside each component. Here we need to collect setColor and call them sequentially when synchronization is required. Combined with the notification , we can easily think that this is a publish-subscribe model. When each component declares the internal state useState(), it is a good opportunity to subscribe. We can customize a hooks useColorState to hijack this operation. As a Controller, maintain the subscription-publishing function:

solve

Through the above analysis, we have clarified the idea: implement a custom hook to intercept the definition of the internal state of each subcomponent, so as to bind the subscription relationship and leave room for operation; the custom hook needs to implement the publish and subscribe function, and when the state changes Notify each component when;

1. Custom hook

export default function useColorState() {const [color, setColor] = useState();return [color, setColor];
} 

2. "Publish-subscribe" model

class ColorChanger {constructor() {this.color = 'orange';this.callBacks = [];}subscribe(cb) {this.callBacks.push(cb);}publish() {this.callBacks.forEach((cb) => cb(this.color));}
} 

3. Subscribe

Complete subscription and internal state initialization:

let colorChanger;

function useColorState() {// ...useEffect(() => {colorChanger = colorChanger || new ColorChanger();colorChanger.subscribe(setColor);setColor(colorChanger.color);}, []);// ...
} 

4. Publish

Here the hook returns setColor which just updates its own state, we need to notify other components:

function useColorState() {// ...const changeColor = (val) => {colorChanger.setColor(val);};return [color, changeColor];
} 
class ColorChanger {// ...setColor(color) {this.color = color;this.publish();}
} 

5. Unsubscribe

When the component is uninstalled, we need to unsubscribe, just delete the setColor of the component from colorChanger.callbacks:

class ColorChanger {// ...unsubscribe(cb) {this.callBacks.splice(this.callBacks.indexOf(cb), 1);}
}

function useColorState() {useEffect(() => {// ...return () => {colorChanger.unsubscribe(setColor);};}, []);
} 

optimization

So far, we have implemented a simple state synchronization function by customizing hook and publish-subscribe mode, but there are still some problems found during debugging:

1. Minor optimizations

setColor(sameColor)Publishing is still triggered during execution , and publishing is a high-cost operation, so optimization is required here:

setColor(color) {if(this.color === color) return;// ...
} 

2. Reducer

Redux is generally an object with complex depth. We only maintain a simple state color of string type here. How does Redux compare whether the old and new states have changed? The answer is Reducer:

The above picture is a reducer, which is very vivid: after filtering through the state comparison, it returns the state that really needs to be changed. Here is the definition of a Reducer:

const newState = reducer(oldState, action); 

So we also transform useColorState:

function colorReducer(state = { color: 'orange' }, action) {return action.color === state.color ? state : { color: action.color };
}

function useColorState(colorReducer) {colorChanger = colorChanger || new ColorChanger(colorReducer);// ...
}

class ColorChanger {setColor(action) {const newState = this.colorReducer(this.state, action);// ...}
} 

The design of Redux Reducer avoids the need for deep traversal to compare the old and new states every time, but returns the old object if it is judged by the reducer that it has not been updated. Since our scene is relatively simple, we directly use ===the comparison . Of course, there are still things that can be further optimized: For example, changeColor should use the useCallback package to prevent unnecessary rendering caused by passing setColor as props. For example, when publishing, a queue should be made to ensure that the current round of notifications is completed before the next round of state changes.

Summarize

This article explores common component state synchronization problems in web development and introduces a simple and practical solution. After reading this article, readers can try to read the React Context implementation, which is similar in principle. In the process of reading the source code, you will find that although the third-party library seems to have a lot of code, the technical principle may not be as complicated as imagined. After mastering the ability to analyze problems and related knowledge, reading source code will be smoother.

at last

Organized 75 JS high-frequency interview questions, and gave answers and analysis, which can basically guarantee that you can cope with the interviewer's questions about JS.



Friends in need, you can click the card below to receive and share for free

Guess you like

Origin blog.csdn.net/Android062005/article/details/129307513
Recommended