Handwritten React Hooks
- Hooks are new in React 16.8 that allow you to use state and other React features without writing a class
- The React API that starts with use is Hooks
What is the Hook
Hook is a special function that lets you “Hook” into the React feature. For example, useState is a Hook that allows you to add state to the React function component.
Why use Hooks
Quote official website description
-
Reusing state logic between components is difficult
You might want to use render props or HOC, but both render properties and higher-order components have a parent container (typically div elements) wrapped around the original component. If you look at React apps in React DevTools, you’ll see that components made up of providers, consumers, high-order components, render props, and other abstraction layers form a nested hell.
-
Complex components become difficult to understand
Components usually get data in componentDidMount and componentDidUpdate. However, the same componentDidMount may also contain a lot of other logic, such as setting up event listeners that need to be cleared later in componentWillUnmount. Code that is related and needs to be modified against each other is split, while completely unrelated code is grouped together in the same method. It’s easy to bug
-
Hard to understand class
This points to the problem: when a parent passes a function to a child, this must be bound
Rules of the Hook
- Call a Hook only from the outermost layer inside a function, never from a loop, conditional judgment, or child function
- Only use the React function to call a Hook. Use the React function component to call other custom hooks
Use ESLint to do hooks rule checking
Use eslint-plugin-react-hooks to check for code errors
{
"plugins": ["react-hooks"].// ...
"rules": {
"react-hooks/rules-of-hooks": 'error'.// Check the Hook rules
"react-hooks/exhaustive-deps": 'warn' // Check for effect dependencies}}Copy the code
useState
UseState returns an array: a state, and a function to update the state.
This is similar to the class component’s this.setstate, but it does not merge the new state with the old state, instead replacing it directly
// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;
function useState(initialState) {
hookStates[hookIndex] = hookStates[hookIndex] || initialState;
// Use closures to maintain function call locations
let currentIndex = hookIndex;
function setState(newState) {
// Check whether the passed state is a function, if prevState is passed
if (typeof newState === "function") {
// copy it again to newState
newState = newState(hookStates[hookIndex]);
}
/ / update the state
hookStates[currentIndex] = newState;
// Triggers a view update
render();
}
// Return an array. Destruct can be written as any variable
return [hookStates[hookIndex++], setState];
}
Copy the code
useEffect
UseEffect is an Effect Hook that gives function components the ability to manipulate side effects. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in the Class component, but has been consolidated into an API
Unlike componentDidMount or componentDidUpdate, effects scheduled with useEffect do not block the browser update view, making your application seem more responsive. In special cases (such as measuring layout), there is a separate useLayoutEffect Hook, used in the same way as useEffect
// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;
function useEffect(callback, dependencies) {
if (hookStates[hookIndex]) {
// Non-initial call
let lastDependencies = hookStates[hookIndex];
// Determine whether the incoming dependency is the same as the last one
let same = dependencies.every(
(item, index) = > item === lastDependencies[index]
);
if (same) {
hookIndex++;
} else{ hookStates[hookIndex++] = dependencies; callback(); }}else {
// Initial callhookStates[hookIndex++] = dependencies; callback(); }}Copy the code
useMemo
Allows you to cache calculations between multiple renders by “remembering” the last one
It makes it easier to control when specific child nodes are updated, reducing the need for pure components
// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;
function useMemo(factory, dependencies) {
if (hookStates[hookIndex]) {
/ / not for the first time
let [lastMemo, lastDependencies] = hookStates[hookIndex];
// Determine whether the incoming dependency is the same as the last one
let same = dependencies.every(
(item, index) = > item === lastDependencies[index]
);
if (same) {
hookIndex++;
return lastMemo;
} else {
// If only one dependent variable is different
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, dependencies];
returnnewMemo; }}else {
// First call
let newMemo = factory();
hookStates[hookIndex++] = [newMemo, dependencies];
returnnewMemo; }}Copy the code
useCallback
ShouldComponentUpdate allows you to keep the same callback reference between rerenders to keep shouldComponentUpdate working
// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;
function useCallback(callback, dependencies) {
if (hookStates[hookIndex]) {
/ / not for the first time
let [lastCallback, lastDependencies] = hookStates[hookIndex];
let same = dependencies.every(
(item, index) = > item === lastDependencies[index]
);
if (same) {
hookIndex++;
return lastCallback;
} else {
// If only one dependent variable is different
hookStates[hookIndex++] = [callback, dependencies];
returncallback; }}else {
// First call
hookStates[hookIndex++] = [callback, dependencies];
returncallback; }}Copy the code
memo
function memo(OldFunctionComponent) {
return class extends React.PureComponent {
render() {
return <OldFunctionComponent {. this.props} / >; }}; }Copy the code
useContext
Receiving a context object (the return value of react.createcontext) and returning the current value of that context useContext(MyContext) simply allows you to read the value of the context and subscribe to changes to the context. You still need to use < myContext.provider > in the upper component tree to provide context for the lower component
function useContext(context) {
return context._currentValue;
}
/ / the parent component
const CountCtx = React.createContext();
function ParentComp() {
const [state, setState] = React.useState({ number: 0 });
return (
<CountCtx.Provider value={{ state.setState}} >
<Child />
</CountCtx.Provider>
);
}
/ / child component
function Child() {
let { state, setState } = useContext(CountCtx);
return (
<div>
<p>{state.number}</p>
<button onClick={()= > setState({ number: state.number + 1 })}>
add
</button>
</div>
);
}
Copy the code
useRef
UseRef returns a mutable ref object whose current property is initialized as the parameter passed in. The ref object returned by useRef remains constant throughout the life of the component, that is, every time a function component is rerendered, The ref object returned is the same (note the use of react. createRef, which recreates the ref each time the component is rerendered)
let lastRef;
function useRef(value) {
lastRef = lastRef || { current: value };
return lastRef;
}
Copy the code
useReducer
The Reducer in useReducer and Redux is like the reducer in useState
// Hold an array of states
let hookStates = [];
/ / index
let hookIndex = 0;
function useReducer(reducer, initialState) {
hookStates[hookIndex] = hookStates[hookIndex] || initialState;
let currentIndex = hookIndex;
function dispatch(action) {
hookStates[currentIndex] = reducer
? reducer(hookStates[currentIndex], action)
: action;
// Triggers a view update
render();
}
return [hookStates[hookIndex++], dispatch];
}
// useState can be overwritten using useReducer
function useState(initialState) {
return useReducer(null, initialState);
}
Copy the code
reference
Rules of the Hook
React Hooks 【 nearly 1W words 】+ project combat
recommended
Get child component instance values from the React Hooks parent component
Elegant use of useRef in React Hooks