Before React introduced Hooks, we needed to implement reuse of component functions, using techniques like HOC and Render Props. A brief word on these two tips, after all we are focusing on Hooks

High order Component (HOC)

High-order component: a function that takes a component and returns a new component. (Many articles say that the new component returned is a high-order component, which is inaccurate.)

Advanced components are mainly used to share component code and enhance component functionality, which is DRY mode. Such as

Class Test extends Component{render() {return (<div> <p>{this.props. Title} - {this.props. Name}</p> {this.props.children} </div> ); Const withTest = Comp => {const name = 'high-order component '; return props => <Comp {... Const withTest = Comp => {return class extends Component{render() {return (props} name={name} />} // const withTest = Comp => {return class extends Component{render() {return () <Comp {... this.props}> <span>this is Hoc</span> </Comp> ); }}}Copy the code

Features:

  1. Higher-order components are pure functions with no side effects;
  2. The new component returned and the component passed in as parameters are two completely separate components;
  3. Don’t use HOC in render methods, unnecessary component offloading and re-rendering can occur, affecting performance;
  4. The Refs of components will not be obtained by higher-order components, so Refs forwarding is required.

Pass parameters to higher-order components

//hoc.tsx export default (wrapProps) => (WrappedComponent) => (props) => ( <WrappedComponent {... props} {... wrapProps} /> ); //index.tsx import HOC from './hoc' const Demo = HOC(props)(demo)Copy the code

Commonly used for high-order component conditional rendering or parameter passing, such as React-Redux’s Connect

High-order component reverse inheritance

Higher-order components inherit from and operate on the input component

export default (WrappedComponent) => { return class Inheritance extends WrappedComponent { componentDidMount() { // You can easily get state and make some more in-depth changes. console.log(this.state); } render() { return super.render(); }}}Copy the code

ES7 decorators use higher-order components

Class Demo extends Component{} withLoading => withHeader, Const enhance = recompact.compose(withHeader,withLoading) from the inside out; @enhance class Demo extends Component{} // You can also use a recompact combination of higher-order components, executed backwardsCopy the code

The advantages and disadvantages

  • Advantages: The logic of the component is reusable, and the function of the component can be enhanced by parameter transfer
  • Disadvantages: Deepening the component nesting level, and it is easy to cause the same name of props when multi-layer higher-order components are nested

PS: You can view the component hierarchy using chrome plug-ins such as React-devtools

Render Props model

Pass a function that returns a render component whose parent determines the render content of the children

class Mouse extends React.Component { constructor(props) { super(props); } render() { return ( <div style={{ height: }}> {this.props. Render (this.state)} //<Cat /> Do not use this hard programming method to write component </div>); }} class MouseTracker extends React.Component {render() {return (<div> <h1> </h1> <Mouse render={name => ( <Cat name={name} /> )}/> </div> ); }}Copy the code

Features:

  1. In the Render Props mode, “Render” can be any name, but if it is children, you need to specify children as function in the propType;
  2. When working with PureComponent, remember not to pass anonymous functions. Because PureComponent is a shallow comparison based on props, it is a new anonymous function every time an update comparison is performed, the shallow comparison returns false, and the child component is re-rendered, thus losing the performance optimization functionality of PureComponent

advantages

The Render Props pattern is a good way to get around HOC’s shortcomings without creating useless components and deepening the hierarchy. However, be aware of the rendering principle of the React component. Improper use of the React component may cause unnecessary overhead in application performance

Hooks appeared

All right, finally, it’s my turn to be the hero of the day. Before we get to Hooks, let’s talk about the weaknesses of the Class component:

  1. The business logic is scattered among the various methods of the component, resulting in repetitive or relational logic. Often need to use hoc, Render prop and other techniques mentioned above to achieve functional reuse, thus deepening the level of component nesting;
  2. The component code written by Class is “heavy,” no matter how small a component is, it must be a Class instance, and there is an obscure this pointing problem (though it can be solved with some Babel Polyfill).

Since [email protected] introduced the Hooks concept, we have been able to write more flexible functional components and implement custom Hooks to meet different business requirements

Commonly used Hooks

useState

Add the state variable for the function component. Variables “disappear” after normal functions exit, while variables in state are retained by React

import React, { useState } from 'react'; Const [count, setCount] = useState(0); function Example() {const [count, setCount] = useState(0); // The useState argument is the initial value of the state variable, which can be a basic data type, array, Return (<div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } ## this. State this. State = {count: 0};Copy the code

Modify the value of state with the operator function returned when defining the state variable

setCount(1); SetState this.setState({count: this.state.count + 1})};Copy the code

⚠️ Note: Unlike setState, updating the state variable always replaces it rather than merges it, so functional updates are required when updating complex structure state values

const [person, setPerson] = useState({name, age}) setPerson(pre => ({... person, age}))Copy the code

useEffect

Provides the ability for function components to perform side effects, such as fetching data, setting up subscriptions, and manually changing the DOM in the React component. UseEffect Hook is equivalent to componentDidMount, componentDidUpdate and componentWillUnmount

function Example() {
  const [times, setTimes] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${times} times`;
  }, [times]);
}
Copy the code
  • Execution mechanism: useEffect Hook is called inside a function component with a callback function (called effect) as the first argument. This callback is executed whenever a function component is rendered (both the first rendering and the update rendering)
  • Effect Cleanup: There may be some memory consuming operations in the effect, such as data subscription, that need to be cleared when the component is uninstalled to free the memory. The effect convention executes the function returned in effect for cleaning before the next effect execution or component uninstallation, as follows
import React, { useState, useEffect } from 'react'; function FriendStatus(props) { const [times, setTimes] = useState(0); useEffect(() => { console.log('effect running', times); return () => { console.log('effect clear'); } }, [times]); const onClickHandle = () => { setTimes(++times) } return (<Button type="default" onClick={onClickHandle}></Button>); } // Effect running //effect clear is printed every time you click on buttonCopy the code
  • Performance optimization: Sometimes you don’t want effect to be executed every time a component is re-rendered, you can pass a second parameter to useEffect. The second argument is an array of dependencies that indicate which data changes effect depends on. The default is []. If the dependent data does not change before and after re-rendering, effect will not be executed

useRef

A ref object is generated, and the ref.current property is initialized as the passed parameter, which remains constant throughout the life of the component, returning the same REF object each time it is rendered

const inputEl = useRef(Date.now());
Copy the code

Unlike createRef() :

  • The ref.current generated by createRef is always null when not used on a DOM element or component, and then points to the actual DOM or component instance.
  • The ref.current generated by useRef is initialized as an incoming parameter and remains constant throughout the life of the component. After binding to the DOM,.current points to the actual DOM

UseImperativeHandle works with the ref forward and forwardRef

useImperativeHandle(ref, createHandle, [deps])
Copy the code

useMemo

Using the Create function and the dependency array, a Memoized value is generated, and the Create function performs a recalculation of the Memoized value only if the value of a dependency changes before and after rendering. Such as:

const memoizedValue = useMemo(() => return Math.sum(a, b), [a, b]);
Copy the code
  • The create function is executed unconditionally on the first rendering;
  • The dependency array defaults to []. Without dependencies, it is executed each time a component is rerendered;
  • Do not perform non-rendering operations in create functions, and remember the difference with useEffect

useCallback

Passing the inline callback function and the array of dependencies as arguments to useCallback returns the Memoized version of the callback function, which is updated only when a dependency changes. Equivalent to useMemo(() => fn, deps) returns the callback function passed in instead of calculating the value

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
Copy the code

MemoizedCallback can be called in certain scenarios without triggering component rerendering

See the React documentation for more information

Custom Hooks

React provides only a limited number of Hooks. In practice, we often need to customize some Hooks. Here is an example of a useFetch custom hook

 export function useFetch(url, options) {
  const [data, setData] = useState({});

  const fetchApi = useCallback(async () => {
    const res = await fetch(url, options);
    if (res.ok && res.status === 200) {
      setData(res.json());
    }
  }, [url]);

  useEffect(() => {
    fetchApi();
  }, [fetchApi]);

  return data;
}
Copy the code