Test Hooks' proficiency
Why can't Hooks be used in for loops and if statements
React.memo,React.useCallback,React. The role and comparison of usememo
The value in useState is an object. If you change the value in the object, will the component render? If you use react It's wrapped in memo()
What is the (Implementation) principle of Hooks?
What is the essence of Hooks? Why?
React Hooks, what convenience does it bring?
How does the useEffect in React Hooks distinguish between life cycle hooks
What is the difference between useEffect(fn, []) and componentDidMount
How's the answer? Before understanding a concept, the more doubts, the deeper the understanding
What is it?
React Hooks is a new feature of React 16.8. It allows you to use state and other react features without writing a class
Why are there Hooks
We must have a concept, that is, what is the essence of react? It is characterized by UI=f(data), everything is a component, and declarative programming. Then, since UI=f(data), data drives UI view changes through function. In business, you can't simply show, but also interact. The interaction will update the state. React changes the state through setState. But this is limited to class components, so before Hooks, functional components were used to render components (also known as puppet components), and class components were used to control state
Then, in order to make the state reuse better, a Mixins ,render props and High order component . Admittedly, although render props and high-level components can be solved, they will bring side effects - components will form a "nested hell"
And the life cycle of class components will make complex components difficult to understand, the learning cost of class syntax and so on, which constitute hooks proposed by React team - let functional components have state management
Official website Three motivations for designing Hooks have also been described:
- Reusing state logic between components is difficult
- Complex components become difficult to understand
- Incomprehensible class
Experiment of state reuse
Mixins Era
It existed before the author used React and has been eliminated
Mixins is a way to extend the collection function. In essence, it copies the properties of one object to another object. However, you can copy any method of any number of objects to a new object, which cannot be realized by inheritance. Its appearance is mainly to solve the problem of code reuse
It is not analyzed here. The official document of React is in Mixins Considered Harmful The article mentioned the harm brought by Mixins:
- Mixins may be interdependent and coupled, which is not conducive to code maintenance
- Methods in different Mixins may conflict with each other
- When there are many Mixins, components can be perceived and even need to be processed, which will cause snowball complexity to the code
Render Props
A simple technique for sharing code between React components using a prop whose value is a function
The component with render prop accepts a function that returns the React element, and calls this function inside the component to implement its own rendering logic
<DataProvider render={data=> ( <h1>Hello, {data.target}</h1> )}>
Details can be found in Official website understand
HOC (high order component)
The principle of HOC is actually very simple. It is a function, and it accepts a component as a parameter and returns a new component. Put the reused place in the high-level component. When you use it, you only need to do different uses
For example: just like giving you a bottle of water, you will drink it when you are thirsty; When you're handsome, you POSE with it; You give someone a drink when they need it, help them
Writing is cheap. Show me code
function Wrapper(WrappedComponent) { return class extends React.Component { componentDidMount() { console.log('I am a bottle of water') } render() { return ( <div> <div className="title">{this.props.title}</div> <WrappedComponent {...this.props} /> </div> ) } } }
import "./styles.css"; import React from "react"; import Wrapper from "./Wrapper"; class A extends React.Component { render() { return <div>Drink it</div>; } } class B extends React.Component { render() { return <div>Play handsome POSE</div>; } } class C extends React.Component { render() { return <div>Help others</div>; } } const AA = Wrapper(A); const BB = Wrapper(B); const CC = Wrapper(C); export default function App() { return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <AA title="I'm an ordinary person" /> <BB /> <CC /> </div> ); }
In this way, we can clearly see the benefits of HOC. "A bottle of water" is a common code. A, B and C process business codes, and then transfer a, B and C into HOC (a bottle of water), returning a new component AA, BB and CC. The same code is shared
You can check it here demo
The use of HOC is not only code reuse, but also permission control, log printing, etc. However, its defects are not obvious. When HOC is widely used, it will produce a large number of nesting, which makes nesting difficult; Moreover, HOC will hijack props, which may cause conflict in case of non-compliance with the agreement
To summarize the HOC:
- Usage: create a function that receives a component as input. In addition to the component, it can also pass other parameters and return a different component based on the component
- Advantages: code reuse, logic reuse
- Disadvantages: nesting makes debugging more difficult; Will hijack props and may cause conflict
The birth of Hooks
The former has the power of state reuse (Mixins is suck out, render props and HOC have side effects). After that, complex components of class components are difficult to understand and maintain (excessive life cycle). this attribute caused by class attribute is also troublesome. So Hooks shouted: I'll come too
It has at least three benefits
Logical reuse
- Second kill render props, hoc
Business codes are more aggregated
- Second kill components
Concise writing
- Second kill components
useState
Function: make function components have the ability to maintain state, and replace the constructor initialization state of class components
example:
const Counter = () => { const [count, setCount] = useState(0) return ( <div onClick={() => setCount(count+1)}>{count}</div> ) }
Features: Logic reuse
When using useState, there are two derivative problems:
1: Capture Value property
stay How are functional components different from class components The values that can be used in the captured rendering function. For example, add three times at the midpoint of the component, pop up the number after setTimeout for 3 seconds, add twice at the midpoint, and display 3 instead of 5 after 3 seconds. But the class component can get the latest data. Why?
Because functional components have the feature of Capture Value. From the perspective of source code, each call to setXX will cause re render to re render the component
If you want to get the latest value, you can save the value in memory through useRef
2: The value in useState is an object. If you change the value in the object, will the component render? How to optimize?
Generally, we use useState to follow a single value as much as possible, but it is inevitable to encounter some special cases. If the value is an object, change one of the attributes in the object and the other attributes remain unchanged, will the components that reference other attributes render?
const DemoSon = (props) => { console.log("render", props); return <div>{props.name}</div>; }; const Demo = () => { const [data, setData] = useState({ foo: { name: "johan", bar: { baz: 1 } } }); const handleClick = () => { setData({ ...data, foo: { ...data.foo, bar: { baz: 2 } } }); }; return ( <div onClick={handleClick}> {data.foo.bar.baz} <DemoSon name={data.foo.name} /> </div> ); };
Click div and modify the value of baz. Will DemoSon render? The answer is yes, why render? Because your reference value changes, a new virtual DOM is generated. When it is rendered to the view, the sub components will render. How to optimize so that components with unchanged data do not render repeatedly? I think there are two ways: one is to split data into foo object and name. Because setData does not change the name, DemoSon will not render. The other is to wrap DemoSon through memo, because memo can avoid re rendering
Can view online demo
useEffect
Function: handle side effects and replace componentDidMount, componentDidUpdate and componentWillUnmount of class components
Usage:
// No second parameter // Both the mount phase and the update phase are executed useEffect(fn) // The second parameter is an empty array // When the mount phase is executed useEffect(fn,[]) // The second parameter is a dependency // Is executed when dependency (deps) data is updated useEffect(fn, [deps]) // Eliminate side effects useEffect(() => { const subscription = props.source.subscribe(); return () => { // Clear subscription subscription.unsubscribe(); }; });
PS: mount stage in the above notes, that is, when the component is loaded; update refers to the change of data (including props)
When using useEffect, you will face several problems:
1. What is the difference between useeffect (FN, []) and componentDidMount?
Although both useEffect(fn, []) and componentDidMount can represent the execution when the component is loaded, they are different in detail. If you want to talk about the details, you need to start from the source code. For details, see this article by the magician Kasong of the source code of React - [what is the difference between use effect (FN, []) and cDM?] ( https://mp.weixin.qq.com/s?__... )Yes, let me talk about my understanding here
In the source code, the rendering of virtual Dom and virtual DOM to real DOM is divided into two stages. The virtual DOM exists in the memory. If the data is added, deleted or modified in JSX, the virtual DOM will label the corresponding data. This stage is called the render stage; The operation of mapping the virtual DOM to the real DOM is called the commit phase, which is responsible for converting these tags into specific DOM operations
In the render phase
- The inserted DOM element is labeled with Placement;
- Update DOM elements are labeled with update tags;
- The deleted DOM element is labeled with Deletion;
- The update ref attribute is labeled Ref
- The useEffect callback is labeled Passive
The commit phase is divided into three sub phases
- Before rendering the view (before mutation stage)
- When rendering a view (mutation phase)
- After rendering the view (layout phase)
If it is labeled with Placement, the corresponding appendChild operation will be executed in the mutation stage, which means that the DOM node is inserted into the view, and then the componentDidMount is called in the layout stage
If it is labeled Passive, it will asynchronously call the callback function of useEffect after the execution of the three sub phases of the commit phase
It can be seen that their call timing is different. useEffect(fn, []) is to call the callback function asynchronously after the execution of the commit phase, while componentDidMount will be called synchronously in the layout phase after the view update (mutation phase) is completed in the commit phase
There is also a hooks in hooks that is called at the same time as componentDidMount - uselayouteeffect
Secondly, useEffect(fn, []) will capture props and state, while componentDidMount will not. The function using useEffect(fn, []) will get the initial props and state, which is the same as capture value
Conclusion: there are two differences: first, the implementation time is different; 2, useEffect(fn, []) will capture props and state
The capture value feature will be described below with a demo
2. Each rendering has its own props and state
Let's talk about rendering first. Let's take a look at a Counter component, Counter
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>click {count} second</p> <button onClick={() => setCount(count + 1)}> click </button> </div> ); }
When rendering for the first time, the initial value of count is obtained from useState(0). When setCount(count + 1) is called, React re renders the component, and the value of count becomes 1. As follows:
// Mount first render function Counter() { const count = 0; // The default is obtained from useState // ... <p>click {count} second</p> // ... } // Update click once function Counter() { const count = 1; // Modify count through setCount // ... <p>click {count} second</p> // ... } // Click Update twice function Counter() { const count = 2; // Modify count through setCount // ... <p>click {count} second</p> // ... }
Whenever we update the status, React re renders the component. Get the count state at the moment (snapshot) every rendering
Values are not captured in class components
for instance:
class ClassDemo extends React.Component { state = { count: 0 }; componentDidMount() { setInterval(() => { this.setState({ count: this.state.count + 1 }); }, 1000); } render() { return <div>I am Class Component, {this.state.count}</div>; }}
The count on the page will be incremented by 1 every second and replaced with functional components
const FunctionDemo = () => { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, []); const handleClick = () => { setCount(count + 1); }; return <div onClick={handleClick}>I am Function Component, {count}</div>;};
Always 1
This is the capture value of hooks. Similar examples are in How are functional components different from class components Introduced
Accessible Online demo see
useLayoutEffect
Effects: side effects of synchronous execution
In most cases, using useEffect can help us deal with side effects, but if you want to synchronously call some side effects, such as DOM operations, you need to use useLayoutEffect. The side effects in useLayoutEffect will be executed synchronously after DOM update
Consistent with the componentDidMount effect in class components, they are synchronously called in the layout phase after the view update (mutation phase) is completed in the commit phase
useCallback
Function: memorize functions and avoid function regeneration. When a function is passed to a subcomponent, you can avoid repeated rendering of the subcomponent
example:
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b],);
Cacheable references
In class components, the binding problem of this often puzzles people
- Use bind in render method
class App extends React.Component { handleClick() { console.log('this > ', this); } render() { return ( <div onClick={this.handleClick.bind(this)}>test</div> ) }}
- Use arrow function in render method
class App extends React.Component { handleClick() { console.log('this > ', this); } render() { return ( <div onClick={e => this.handleClick(e)}>test</div> ) }}
- bind in constructor
class App extends React.Component { constructor(props) { super(props); this.handleClick = this.handleClick.bind(this); } handleClick() { console.log('this > ', this); } render() { return ( <div onClick={this.handleClick}>test</div> ) }}
4. Use arrow function binding in the definition phase
class App extends React.Component { handleClick = () => { console.log('this > ', this); } render() { return ( <div onClick={this.handleClick}>test</div> ) }}
The first three will trigger rendering again due to changes in props or state of App components to render new handleclicks. The fourth method takes handleClick out and assigns it as a variable, and points to the storage function through this, which plays the role of cache
Functional components will render
function App() { const handleClick = () => { console.log('Click'); } return ( <div onClick={handleClick}>test</div> )}
But useCallback can cache the function and make it "remember"“
function App() { const handleClick = useCallback(() => { console.log('Click'); }, []) return ( <div className="App"> <Demo handleClick={handleClick} /> </div> )}
But useCallback must use shouldComponentUpdate or react Memo to ignore the same parameters and render repeatedly
Therefore, useCallback cannot be used alone. It needs to be connected with react Memo fit
function Demo(props) { return <div onClick={props.handleClick}>test</div>;}const MemoDemo = memo(Demo);function App() { const handleClick = useCallback(() => { console.log('Click'); }, []) return ( <div className="App"> <Demo handleClick={handleClick} /> </div> )}
However, useCallback will make the code less readable, so try not to use it
How to improve performance without useCallback?
useMemo
Function: memory component. Replace shouldComponentUpdate of class components
The function of useCallback can be replaced by useMemo. If you want to return a memory function through useMemo, it is also possible
useCallback(fn, deps) is equivalent to usememo (() = > FN, DEPs)
example:
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
By default, if the React parent component is re rendered, all of its child components will be re rendered, even if the child components have not changed
The parameters accepted by useMemo and useCallback are the same. They are executed after their dependencies change and return cached values. The difference is that useMemo returns the result of function operation and useCallback returns the function
useMemo returns a value to avoid expensive calculations at each render
useCallback VS useMemo
Similarities: useCallback and useMemo are both means of performance optimization, similar to shouldComponentUpdate of class components. shouldComponentUpdate is used in sub components to judge whether the props and state of the component have changed, so as to avoid re rendering the sub components every time the parent component renders
Difference: the difference between useCallback and useMemo is that useCallback returns a function. When this function is used as a child component, it can avoid re rendering this child component every time the parent component is updated
memo
Effect: avoid re rendering
Sub components are re rendered only when props changes
After being wrapped by memo, when props remains unchanged, the sub components will not render
React. The memo () method can prevent sub components from rendering unnecessarily, thus providing component performance.
About performance optimization, Dan once wrote an article: Before you write memo() In fact, before we use useCallback, useMemo and memo, we might as well try moving down the state and improving the content. The purpose is to make components that do not need to be rendered do not render repeatedly
useRef
effect:
Save reference values, similar to createRef. We are used to saving DOM with ref
Using useRef to save and update some data has certain advantages. It can save data without memory, so that these data will not be cleared when re rendering
It is not only used to manage DOM ref, but also equivalent to this. It can store any variable and solve the inconvenience caused by closures
If we want to use ordinary variables to track data changes during re rendering, it is not feasible, because it will be re initialized every time the component is rendered. However, if ref is used, the data in it can remain unchanged every time the component is rendered.
example:
const [count, setCount] = useState<number>(0)const countRef = useRef<number>(count)
stay How are functional components different from class components Introduced the use method
other
useContext: reduce component level
useReducer: a method similar to redux. useState is based on its expansion
ForwardRef: forward ref
useImperativeHandle: transparently pass Ref, and the parent component gets the methods in the child component
Customize Hooks
Because useState and useEffect are function calls, we can combine them into our own Hooks
function MyResponsiveComponent() { const width = useWindowWidth(); return ( <p> Window width is {width}</p> )}function useWindowWidth() { const [width, setWidth] = useState(window,innerWidth); useEffect(() => { const handleResize = () => setWidth(window.innerWidth) window.addEventListener('resize', handleResize) return () => { window.removeEventListener('resize', handleResize) } }) return width;}
Custom Hooks allow different components to share reusable state logic. Note that the state itself is not shared. Each call to Hook only declares its own independent state
Deficiency of React Hooks
Although the functions of most class components are implemented, the two API s getSnapshotBeforeUpdate and componentDidCatch cannot be implemented
Appendix: rules of use
Hooks is essentially a JavaScript function, which should be followed when using it Two rules
Use Hook only at the top
Do not call Hook in loops, conditions or nested functions, make sure that they are always invoked before the top level of your React function and any return. By following this rule, you can ensure that hooks are called in the same order in each rendering. This allows react to maintain the correct hook state between multiple useState and useEffect calls
Call Hook only in React function
Instead of calling Hook in the ordinary JavaScript function, you can:
- Calling Hook in the function component of React
- Call other Hook in custom Hook
reference material
- [React] from Mixin to HOC and then to Hook
- useEffect Complete Guide
- useCallback, useMemo analysis and differences
- Ten case societies React Hooks
- What is the difference between useeffect, usecallback and usememo
- React hooks best practices [updating]
- React Hooks 10000 word summary
- "Comprehensive analysis of React 10000 words foundation"
- Separation of concerns with React hooks
- How to make rational use of React hook?
- Use react Memo () improves component performance
- react-render-always-rerenders
- What is the difference between [useEffect(fn, []) and cDM?] ( https://mp.weixin.qq.com/s?__...)
- Before you write memo()