React Hook? How does React Hook work

React-hooks are improvements that the React team gradually realized during the React component development practice, which actually involves thinking and focusing on class components and function components.

(1) Class component: The React component is derived from react.component.react based on ES6 Class. Here is a class component:

class DemoClass extends React.Component { state = { text: "" }; componentDidMount() { //... } changeText = (newText) => { this.setState({ text: newText }); }; Render () {return (<div className="demoClass"> <p>{this.state.text}</p> <button onClick={this.changetext}> modify </button> </div> ); }}Copy the code

As you can see, the React class component has quite a few “things off the shelf” built-in for scheduling/customization. State and lifecycle are typical examples of these “things off the shelf”. It’s not that hard to get these things, just inherit a react.component.

Of course, this is one of the inconveniences of class components, which are too cumbersome to write a class component to solve many problems. Complex poses inevitably impose a high cost of understanding, which we don’t want to see. In addition, the logic written by the developer is stuck to the component after encapsulation, which makes it difficult to split and reuse the logic inside the class component.

(2) Function component: Function component is the React component in the form of a function. In the early days, react-hooks did not exist. Function components could not define and maintain state internally, hence the name “stateless component”. Here is a function component:

Function DemoFunction(props) {const {text} = props return (<div className=" DemoFunction "> <p>{' [${text}]`}</p> </div> ); }Copy the code

Compared to class components, functional components have obvious features such as lightness, flexibility, ease of organization and maintenance, and lower learning costs.

By comparison, the two types of components can be distinguished morphologically. The differences between them are as follows:

  • Class components need to inherit class, function components do not;
  • Class components have access to lifecycle methods, function components do not;
  • Class components can get instantiated this and do all sorts of things based on this, but function components can’t;
  • State can be defined and maintained in class components, but not in function components;

In addition, there are some other differences. By the above differences, we can’t say who is good and who is bad, they each have their own advantages. Before react-hooks, class components had significantly stronger capability boundaries than function components.

In fact, between the class component and the function component, isObject-oriented and functional programmingThe difference between these two different sets of design ideas. Function components fit more closely with the React framework design philosophy:

The React component itself is positioned as a function, a function that inputs data and outputs the UI. As developers, we write declarative code, and the React framework’s main job is to convert declarative code into imperative DOM operations in a timely manner, mapping data-level descriptions to user visible UI changes. This means that, in principle, React data should always be tightly bound to render, whereas class components can’t. The function component actually binds the data to the render. A functional component is a component representation that better matches its design concept and is more conducive to logical separation and reuse.

To make it easier for developers to write functional components. That’s where react-hooks come in.

React-hooks are a set of “Hooks” that make function components more powerful and flexible.

Functional components have much less than class components, such as life cycles, management of state, and so on. This brings many limitations to the use of functional components, and makes it impossible to write a truly functional component in the form of a function. React-hooks are used to help function components fill in these missing capabilities (as opposed to class components).

If a function component is a lightweight speedboat, react-hooks are a rich box of parts. “Reshipment battleship” preset by the equipment, the basic all have the box, all at the same time it also don’t force you to, but allows you the freedom to choose and use the ability you need, and then the ability to Hook (Hook) in the form of a “Hook” into your component, to customize an “exclusive” battleship is best for you.

Why does useState use arrays instead of objects

useState

const [count, setCount] = useState(0)
Copy the code

You can see that useState returns an array, so why return an array instead of an object?

Destruct assignment is used here, so let’s take a look at ES6 destruct assignment first:

Destruct assignment of arrays

const foo = [1, 2, 3]; const [one, two, three] = foo; console.log(one); // 1 console.log(two); // 2 console.log(three); // 3 Copy codeCopy the code

Object destructuring assignment

const user = {
  id: 888,
  name: "xiaoxin"
};
const { id, name } = user;
console.log(id);	// 888
console.log(name);	// "xiaoxin"
Copy the code

After looking at these two examples, the answer should come out:

  • If useState returns an array, the user can name the elements in the array, and the code looks cleaner
  • If useState returns an object, the destruct object must have the same name as the object returned by the internal implementation of useState. If you want to use the object more than once, you must set an alias to use the returned value

Let’s see what happens if useState returns an object:

Const {state, setState} = useState(false); Const {state: counter, setState: setCounter} = useState(0)Copy the code

As you can see, the use of the return object is cumbersome, and it will be used more frequently in real projects. Conclusion: useState returns an array instead of an object to reduce the complexity of using it. If an array is returned, it can be destructed in order. If an object is returned more than once, you need to define an alias.

What problems do React Hooks fix?

React Hooks primarily address the following issues:

(1) It is difficult to reuse state logic between components

React does not provide a way to “attach” reusability behavior to components (for example, to connect components to a store) to address such issues using render props and higher-order components. But such scenarios require reorganizing the component structure, which can be cumbersome and make the code difficult to understand. Components made up of providers, consumers, high-level components, render props, and other abstraction layers form a nested hell. Although they can be filtered out in DevTools, this illustrates a deeper problem: React needs to provide a better native way to share state logic.

Hooks can be used to extract state logic from components so that it can be individually tested and reused. Hooks allow us to reuse state logic without modifying the component structure. This makes it easier to share hooks between components or within a community.

(2) Complex components become difficult to understand

Within components, each lifecycle often contains some unrelated logic. For example, components often 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. This is very buggy and leads to logical inconsistencies.

In most cases, it is not possible to break components down into smaller granularity because state logic is ubiquitous. This makes testing a bit of a challenge. This is also one of the reasons many people use React in conjunction with the state management library. However, this often introduces a lot of abstraction and requires you to switch back and forth between different files, making reuse more difficult.

To solve this problem, hooks break the interrelated parts of a component into smaller functions (such as setting up subscriptions or requesting data) rather than enforcing a lifecycle partition. You can also use Reducer to manage the internal state of components and make them more predictable.

(3) Difficult to understand the class

In addition to the difficulties of code reuse and code management, class is a big barrier to learning React. We have to understand how this works in JavaScript, which is vastly different from other languages. And don’t forget to bind event handlers. Without a stable syntax proposal, the code is very redundant. You can understand props, state, and the top-down data flow pretty well, but you can’t do anything about class. Even experienced React developers disagree about the difference between a function component and a class component, and even how the two components are used.

To address these issues, hooks allow you to use more React features in non-class situations. Conceptually, the React component has always been more like a function. Hook embraced functions without sacrificing the spirit of React. Hooks provide solutions to problems without learning complex functional or reactive programming techniques

What are the limitations of React Hook?

React Hooks have two main limitations:

  • Do not call hooks in loops, conditions, or nested functions;
  • Call a Hook in the React function component.

So why is there such a limit? Hooks are designed to improve the React component development pattern. There were three problems with the old development model.

  • It is difficult to reuse state logic between components. Common solutions in the past were higher-order components, render props, and state management frameworks.
  • Complex components become difficult to understand. Lifecycle functions are so deeply coupled to the business logic that the associated parts are difficult to disassemble.
  • Humans and machines can easily confuse classes. The React team also has some problems with classes that are difficult to optimize. We hope to make some improvements in compilation optimization.

These three issues have somewhat hindered React’s future development, so in order to solve these three issues, Hooks started the design based on function components. The third issue, however, determines that Hooks only support function components.

Why not call a Hook in a loop, condition, or nested function? Because Hooks are designed to be implemented based on arrays. If a loop, conditional, or nested function is used, it is possible to misplace the value of the array and execute the wrong Hook. Of course, the React source code is not an array, it’s a linked list.

These limitations can impose a certain level of mental overhead on the code that beginners can make mistakes, which can be prevented by introducing Hooks checks for ESLint.

UseEffect and useEffect difference

(1) Common ground

  • Effects: useEffect and useLayoutEffect are both used to deal with side effects, such as changing the DOM, setting up subscriptions, manipulating timers, etc. Manipulating side effects inside function components is not allowed, so you need to use these two functions to handle them.
  • UseEffect and useLayoutEffect both of the underlying function signature is completely the same, are called mountEffectImpl method, there is no difference in use, basically can be directly replaced.

(2) Differences

  • UseEffect is invoked asynchronously during React rendering and is used in most scenarios. UseLayoutEffect is called synchronously after all DOM changes, mainly to handle DOM manipulation, styling, avoiding page flickering, and so on. Because it is synchronous processing, you need to avoid blocking on useLayoutEffect by doing computationally expensive and time-consuming tasks.
  • UseEffect: The code is executed sequentially, after changing the screen pixels (rendering first, then changing the DOM). It may flicker when changing the screen contents; UseLayoutEffect is executed before changing screen pixels (postponing the page display event, changing the DOM first and rendering later), without flickering. UseLayoutEffect is always executed before useEffect.

In the future, the two apis will coexist for a long time. There is no plan to cut or merge them at the moment. Developers need to choose according to the scenario. The React team’s advice is very practical. If you are really confused, useEffect first. If the page is abnormal, replace it with useLayoutEffect.

React Hooks Problems and causes during development

(1) Never call a Hook in a loop, conditional, or nested function. You must always use the Hook at the top of the React function

This is because React needs to use the call order to properly update the corresponding state and call the corresponding hook functions. Once a Hook is called in a loop or conditional branch statement, it is easy to cause inconsistencies in the order of calls, with unpredictable consequences.

(2) When using useState, use push, pop, splice and so on to directly change the pit of the array object

Using push to change the array directly does not get the new value and should be destructed, but this is not a problem in class. Code examples:

Function Indicatorfilter() {let [num,setNums] = useState([0,1,2,3]) const test = () => [...num,1] num.push(1) // num = [...num,1] setNums(num)} return (<div The className = 'filter' > < div onClick = {test} > test < / div > < div > {num. The map ((item, index) = > (< div key = {index} > {item} < / div >))} </div> </div> ) } class Indicatorfilter extends React.Component<any,any>{ constructor(props:any){ super(props) This.state = {nums:[1,2,3]} this.test = this.test.bind(this)} test(){this.state.nums.push(1) {this.state = {nums:[1,2,3]} this.test = this.test.bind(this)} test(){ this.setState({ nums: This.state.nums})} render(){let {nums} = this.state return(<div> <div onClick={this.test}> test </div> <div> {nums.map((item:any,index:number) => ( <div key={index}>{item}</div> ))} </div> </div> ) } }Copy the code

(3) When you set the status of useState, it takes effect only for the first time. You must use useEffect to update the status later

TableDeail is a public component. In the parent component that calls it, we change the value of columns through set, thinking that the columns passed to TableDeail are the latest value, so tabColumn is the latest value every time, but in fact tabColumn is the original value. Columns are not updated with updates:

const TableDeail = ({ columns, }:TableData) => { const [tabColumn, SetTabColumn] = useState(columns)} const TableDeail = ({columns, }:TableData) => { const [tabColumn, setTabColumn] = useState(columns) useEffect(() =>{setTabColumn(columns)},[columns]) }Copy the code

(4) Make good use of useCallback

UseMemo may be used if we do not have any parameter changes when the parent component passes to the child component event handle. But every time the parent renders the child, it does so even if it doesn’t change.

(5) Don’t abuse useContext

A state management tool based on useContext encapsulation is available.

React Hooks relate to the lifecycle?

Function components are functions in nature, there is no concept of state, so there is no lifecycle, just a render function. This is different with the introduction of Hooks, which allow components to have state without using classes, so the concept of a lifecycle is useState, useEffect(), and useLayoutEffect().

That is, Hooks components (function components that use Hooks) have a life cycle, while function components (function components that do not use Hooks) have no life cycle.

Here is the specific class/Hooks lifecycle mapping:

  • constructorFunction components do not need constructors and can be calledUseState to initialize state. If the computation is expensive, you can also pass a function touseState.
const [num, UpdateNum] = useState(0)

Copy the code
  • getDerivedStateFromProps: In general, we do not need to use it, can be inUpdate state during renderingTo achieve the realizationgetDerivedStateFromPropsThe purpose of.
function ScrollView({row}) { let [isScrollingDown, setIsScrollingDown] = useState(false); let [prevRow, setPrevRow] = useState(null); if (row ! PrevRow == prevRow) {// Row has changed since last render. Update the isScrollingDown. setIsScrollingDown(prevRow ! == null && row > prevRow); setPrevRow(row); } return `Scrolling down: ${isScrollingDown}`; }Copy the code

React immediately exits the first render and re-runs the component with the updated state to avoid too much performance cost.

  • shouldComponentUpdate: you can useReact.memoWrap a component to itspropsShallow comparison
Const Button = React. Memo ((props) => {// specific components});Copy the code

Note: React.memo is equivalent to PureComponent, which only compares props. Here you can also use useMemo to optimize each node.

  • renderThis is the body of the function component itself.
  • componentDidMount.componentDidUpdate:useLayoutEffectThe call phase is the same as the two. However, we recommend youStart with useEffectTry using it only when something goes wronguseLayoutEffect.useEffectYou can express all of these combinations.
// componentDidMount useEffect(()=>{componentDidMount useEffect()=>{componentDidMount useEffect()=>{componentDidMount useEffect()=>{ Document. title = 'You clicked ${count} times'; Return () => {// required when count changes componentDidUpdate (prior to document.title =... }, [count]);}, [count], [count]); // Update only when count changesCopy the code

Remember React will wait for the browser to finish rendering the image before delaying the call, thus making extra operations easy

  • componentWillUnmount: the equivalent ofuseEffect Inside returnedcleanupfunction
/ / componentDidMount componentWillUnmount useEffect (() = > {/ / need to return the contents of componentDidMount executive function the cleanup () {/ / Need to execute at componentWillUnmount}}, [])Copy the code
  • componentDidCatch and getDerivedStateFromError: currently,Has not yet beenThe Hook equivalents for these methods, but will be added soon.
The class components Hooks components
constructor useState
getDerivedStateFromProps Update function in useState
shouldComponentUpdate useMemo
render The function itself
componentDidMount useEffect
componentDidUpdate useEffect
componentWillUnmount UseEffect is the function returned in useEffect
componentDidCatch There is no
getDerivedStateFromError There is no