History of React Hook and Pits in Actual Combat
Hook
The React team has focused on code reuse from the beginning
Their solutions to reuse code: Mixin, HOC, Render Prop, and now Custom Hook
Therefore, Custom Hook is not a product of a slap in the head, even many developers with rich development experience in Custom Hook do not understand how Hook came to be and what role it plays in React
It is impossible to deeply understand Custom Hook without understanding this design idea. Today we will learn together
1.
This method is very common in Vue2 and is also an important part of its source code, such as the merge of the parent child component lifecycle function
1 | var SetIntervalMixin = { |
Advantages:
- It does play the role of reusing code
Disadvantages:
- It is an implicit dependency, and implicit dependencies are considered bad in React
- Name conflict issues
- Can only work in’React.createClass’, does not support ES6 Class Component
- Found in practice: difficult to maintain
It has been marked as’not recommended 'on the official website of React, the official complain point这里
2.
Beginning in 2015, the React team announced that it did not recommend using Mixin, and recommended that everyone use the HOC mode
HOC adopts’decorator pattern 'to reuse code
1 | function withWindowWidth(BaseComponent) { |
The classic separation of container presidential components starts here
The following is the most classic HOC container component and display component separation, case - Redux in the connect example code
1 | export const createInfoScreen = (ChildComponent, fetchData, dataName) => { |
Advantages:
- Can work in any component including Class Component
- The principle of separating container components from display components, which it advocates, has been achieved: separation of concerns
Disadvantages:
- Not intuitive and difficult to read
- Name conflicts
- Components are nested layer by layer
3.
Since 2017, Render Prop has become popular
Render Prop uses’proxy mode 'to reuse code
1 | class WindowWidth extends React.Component { |
React Router also uses this API design:
1 | <Route path = "/about" render= { (props) => <About {...props} />}> |
Advantages:
- Be flexible
Disadvantages:
- Difficult to read, difficult to understand
4.
In 2018, the React team announced a new way to reuse code - React Hook
Its core change is to allow function components to store their own state, before which function components cannot have their own state
This change allows us to abstract the logic of a React component just as we abstract a normal function
Principle of implementation: closure
1 | import { useState, useEffect } from "react"; |
Advantages:
- Extracting logic is very easy
- Very easy to combine
- Very readable
- No name conflict issues
Disadvantages:
- Hook has its own usage restrictions: it can only be used at the top level of the component, and it can only be used in the component
- Since the principle is closure, there will be difficult to understand problems in rare cases
Several points to pay attention to during the use of Hook
Closure issues
1 | function WatchCount() { |
What do you think this code will print?
That’s right, every time it prints out “Count is 0”
Because when we set the timer, we pass in a function closure, which has already cached the value of count, so we call the same log function every time.
The solution is also very simple
1 | function WatchCount() { |
This tells useEffect to recreate the side effect function whenever the count changes.
The closure problem is not just a useEffect problem, but a common problem for all hooks that may use function closures.
useState The second parameter to modify state is asynchronous and needs to take effect on the next render
Hook’s infinite loop rendering problem
1 | export default function App() { |
Let's analyze why obj triggers infinite rendering when it is a normal object
The first time rendering, the side effect of executing useEffect will call setNum to update num
Calling setNum will update num, causing the component to be re-rendered. At this time, obj is already another object (although the property and value are exactly the same, the address has changed, and the way react compares useEffect is very rude, it should also be for performance reasons, just a simple judgment), so it will re-execute setNum, and it will start to re-render, so it enters an infinite loop.
Understanding why obj is infinitely rendered when it is an object, we can understand why obj does not enter an infinite loop when it is a basic type.
When obj is a state, unless the corresponding setState is manually called, react will not consider the state to have changed.
useState lazy initialization
If the first parameter is passed to a function, the function is not used as the initial value of the state, but the return value of the function as the initial value, which is called lazy initialization. So try not to use useState to save function.
The value returned by useRef is not the component state, so its change will not trigger re-rendering, and it will return the same value every time it is rendered
In essence, useRef is like a “box” that can hold a mutable value in its .current property.
You should be familiar with this kind of ref访问 DOM If you pass a ref object to a component as < div ref = {myRef}/>, React will set the ref object’s .current property to the corresponding DOM node no matter how the node changes.
However, useRef () is more useful than the ref attribute. It can很方便地保存任何可变值, which is similar to the way instance fields are used in a class.
This is because it creates a normal Javascript object. The only difference between useRef () and a self-built {current:…} object is that useRef returns the same ref object every time it is rendered.
Remember that useRef * does not * notify you when the ref object content changes. Changing the .current property does not trigger component re-rendering. If you want to run some code when React binds or unbinds the ref of a DOM node, you need to use回调 ref To achieve.
That is to say, this code will not trigger re-rendering even if you click setCallback, so clicking call callback calls the original callback.
You can change the onClick of call callback to () => callbackRef.current ()
Note that changing it to onclick = {callbackRef.current} is invalid. This is no different from the form on the figure.
If you want to return a function in a custom hook, you can wrap it with useCallback
1 | const memoizedCallback = useCallback( |
Returns a memoized Callback function.
Pass the internal connection callback function and an array of dependencies as arguments to useCallback, which will return a memoized version of the callback function that will only be updated when a dependency changes. It is useful when you pass the callback function to a child component that is optimized and uses reference equality to avoid unnecessary rendering (such as shouldComponentUpdate).
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
。
Attention
The dependency array will not be passed as a parameter to the callback function. Although conceptually it shows that all values referenced in the callback function should appear in the dependency array. The compiler will be more intelligent in the future, and it will be possible to create arrays automatically.
We recommend enabling
Reference link: https://www.notion.so/10-7-React-Hook-14e8a28e607f45e2a4787ad9cebbbe66