Learn how to use React Hook 😀😀😀. Learn how to use React Hook

Outline:

  • React Different component usage.
  • React Hook brings more than ever before.
  • React hook

React component development

1. Functional (stateless) components

Functional (Stateless) Component, also called Stateless Component, is usually only responsible for rendering.

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
Copy the code

2. Class (stateful) components

A Class (Stateful) Component is also a Stateful Component, which is also called a container Component. There is interaction logic and business logic in general.

class Welcome extends React.Component {
	state = {
		name: 'tori,}componentDidMount(){the fetch (...). ; ... }render() {
    return (
		<>
			<h1>Hello, {this.state.name}</h1>
			<button onClick={()= >Enclosing setState ({name: "007"})} > change name</button>
		</>); }}Copy the code

3. Render components

Presentational Component, similar to functional (stateless) components.

const Hello = (props) = > {
  return (
    <div>
      <h1>Hello! {props.name}</h1>
    </div>)}Copy the code

**📢 Summary: **

  • Function components must be stateless components, presentation components are generally stateless components;
  • A class component can be either stateful or stateless.
  • Container components are generally stateful components.
  • The principle of division is summarized as: divide and rule, high cohesion, low coupling;

Most of the requirements can be met through a combination of the above components.

4. Advanced components

Higher order components (HOC)

HOC is mainly a state of extraction. It extracts the logic of repeated controlled components into higher-order components and passes it to the controlled components with new props. In the higher-order components, the controllable components are passed in to the operating props. Higher-order components commonly found in open source libraries: Redux’s Connect, React-Router’s withRouter, etc.

Class HocFactory extends React.Component {
    constructor(props) {
      super(props)
    }
	/ / props to operate
	/ /...
    render() {
      constNewProps = {... };return (Component) = > <Component{...newProps} / >;
    }
} 
Const Authorized = (Component) = > (permission) = > {
	return Class Authorized extendsReact.Com ponent {...render() {
			constIsAuth = ' ';return isAuth ? <Component /> : <NoMatch />; }}}// The higher-order components involved in the project
// The main function is to delegate all actions to component Pro via higher-order components.
import { bindActionCreators } from"Story";import { connect } from'the react - story'; Import * as actions from './actions'; // Cache actions to avoid render reloading let cachedActions; // Action binds dispatch via bindActionCreators, const bindActions = (Dispatch, ownProps) => {if (! cachedActions) { cachedActions = { dispatch, actions: bindActionCreators(actions, dispatch), }; } return cachedActions; }; const connectWithActions = ( mapStateToProps, mergeProps, options ) => (component) => connect( mapStateToProps, bindActions, mergeProps, options )(component); export default connectWithActions; // There are log-based middleware and so on.Copy the code

The shortage of HOC

  • HOC creates many useless components, deepening the component hierarchy, and affecting performance and debugging.
  • Multiple hogs are nested at the same time, hijacking props. The naming may conflict, and internally it is impossible to determine which hogs are derived from.

5. Render Props

Render Props you can think of this as JavaScript callback functions

// Implement a higher-order component that controls Modal Visible
class ToggleVisible extends React.Component {
	state = {
		visible: false
	};
	toggle = () = > {
		this.setState({visible:!this.state.visible});
	}
	render() {
		return (
		    <>{this.props.children({visible, toggle})}</>); }}/ / use
const EditUser = () = > (
	<ToggleVisible>
		{({visible, toggle}) => (<>
		    <Modal visible={visible}/>
		    <Button onClick={toggle}>Turn on/off Modal</Button>
		</>)}
	</ToggleVisible>
)
Copy the code

📢 advantages

  • Component reuse does not create redundant nodes, that is, no redundant nesting.
  • Don’t worry about naming props.

6. Compound components

The props needed by the child component are encapsulated by the parent component, so there is no need to pass all props when referencing the child component. Two methods for composing component cores are react.children. map and react.cloneElement.

For example, the click event required by the following child component is transferred to the parent component and encapsulated internally by the parent to the child component. Many group components in Ant-Design use this method.

Refer to the article

class GroupButton extends React.PureComponent {
  state = {
    activeIndex: 0
  };
  render() {
    return (
      <>
        {React.Children.map(this.props.children, (child, index) =>
          child.type
            ? React.cloneElement(child, {
                active: this.state.activeIndex === index,
                onClick: () => {
                  this.setState({ activeIndex: index });
                  this.props.onChange(child.props.value);
                }
              })
            : child
        )}
      </>); }}/ / usage
<GroupButton
  onChange={e= > {
    console.log (" onChange ", e); }} ><Button value="red">red</Button>
  <Button value="yellow">yellow</Button>
  <Button value="Blue">blue</Button>
  <Button value="white">white</Button>
</GroupButton>
Copy the code

🙊 End of nonsense, start to get down to business…

👍 React hooks

Before hooks, it was difficult to reuse state logic between components, solutions (HOC, Render Props) needed to reorganize component structures, and the code was difficult to understand. Observe React applications in React DevTools, and you’ll see that components made up of providers, consumers, high-order components, render props, and other abstraction layers form a nested hell.

Component maintenance becomes more complex, such as event-listening logic that has to be bundled and unbundled in different life cycles, and complex pages (componentDidMount) that contain a lot of logic make code readable.

The this in the class component is hard to understand, and the class does not compress well, and makes hot overloading unstable. See official introduction for more introduction.

So hook comes to solve these problems:

  • Avoid hell nesting and improve readability.
  • Functional components that are easier to understand than classes.
  • The class component life cycle is too many and too complex, causing functional components to exist statically.
  • Address the shortcomings of HOC and Render Props.
  • UI and logic are easier to separate.

The following describes the official Hook apis one by one.

1. useState

The 📢 function component is stateful

const [state, setState] = useState(initialState); State is a variable, setState is a method to modify the value of state, setState is also executed asynchronously.

DEMO1

Class this.setState update is state merge, setState is replacement in useState.

function Example() {
  // Declare a state variable called "count"
  const [count, setCount] = useState(0);
	const [obj, setData] = useState();
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={()= > setCount(count + 1)}>Click me</button>
    </div>
  );
}
Copy the code

2. useEffect

📢 Forget the life cycle, remember the side effects

useEffect(()  =>  {// Async Action}, ? [dependencies]); // The second parameter is optional
Copy the code

DEMO2

function Hook2() {
  const [data, setData] = useState();
  useEffect(() = > {
    console.log("useEffect");
  });
  return (
    <div>{(() => { console.log("render"); return null; }}) ()<p>data: {JSON.stringify(data)}</p>
    </div>
  );
}
Copy the code

Execution Result:

Conclusion:

  • UseEffect is executed after render.

DEMO3

import React, { useState, useEffect, useRef } from"React";function Demo3() {
  const [data, setData] = useState();
  useEffect(() = > {
    console.log("UseEffect - []"); Fetch (" https://www.mxnzp.com/api/lottery/common/latest?code=ssq "). Then (res = > res. Json ()). Then (res = > {setData (res); }); } []); useEffect(() => { console.log("UseEffect --> No dependencies"); }); UseEffect (() => {console.log(" useEffect depends on data: data has changed "); }, [data]); return ( 
      

data: {JSON.stringify(data)}

); } export default Demo3;
Copy the code

Execution Result:

Conclusion:

  • Effect is executed sequentially after render.
  • Effect executes sequentially each time after render without any dependencies.
  • Effect internal execution is asynchronous.
  • Rely on[]You can do something like thatcomponentDidMountBut it’s best to forget the life cycle and just remember the side effects.

DEMO4

import React, { useState, useEffect, useRef } from "react";

function Demo4() {
  useEffect(() = > {
    console.log (" useEffect1 ");const timeId = setTimeout(() = > {
      console.log (" useEffect1 -setTimeout-2000"); },2000);
    return () = > {
      clearTimeout(timeId); }; } []); useEffect(() = > {
    console.log("useEffect2");
    const timeId = setInterval(() = > {
      console.log("useEffect2-setInterval-1000");
    }, 1000);
    return () = > {
      clearInterval(timeId); }; } []);return (
    <div>= > {{(). The console log (" render "); return null; }}) ()<p>demo4</p>
    </div>
  );
}
export default Demo4;
Copy the code

Execution Result:

Conclusion:

  • The effect callbacks are executed simultaneously in sequence.
  • The effect callback returns an anonymous function equivalent tocomponentUnMountRemove eventLisenter, clear timeId, etc., to prevent memory leaks after component uninstallation.

To sum up, useEffect is a hook function in an existing function component that listens for a callback whenever a dependency changes.

3. useContext

Hook functions that share data across components

const value = useContext(MyContext);
// MyContext is the context object (the return value of React. CreateContext)
// useContext returns the return value of MyContext.
// The current context value is determined by the < myContext. Provider> value prop of the upper-layer component closest to the current component.
Copy the code

DEMO5

import React, { useContext, useState } from"React";const MyContext = React.createContext();
function Demo5() {
  const [value, setValue] = useState("Init"); The console. The log (" Demo5 "); return ( 
      
{(() => { console.log("
render"); return null; } < button onClick = {}) () () = > {the console. The log (' click: Update value') setValue(' ${date.now ()}_newValue ')}}> Change value < myContext.provider value={value}> ); } function Child1() { const value = useContext(MyContext); The console. The log (" Child1 - value ", value); return
Child1-value: {value}
; } function Child2(props) {console.log(' Child2 ') return
Child2
; }
Copy the code

Execution Result:

Conclusion:

  • UseContext components always re-render when the context value changes, so the more wrapped < myContext. Provider>, the deeper the hierarchy, the performance impact.

  • When the value of < myContext. Provider> changes, all components are rerendered regardless of whether the component subscribed to content Value.

  • Child2 in demo should not rerender, how to avoid unnecessary render? * Use react.Memo optimization.

const Child2 = React.memo((props) = > {
  return <div>Child2</div>;
})
Copy the code

Execution Result:

Note: By default, React. Memo only performs shallow comparisons on complex objects. If you want to control the comparison process, pass in your custom comparison function as a second argument. Refer to the link

4. useRef

portal

const refContainer = useRef(initialValue);
Copy the code
  • UseRef returns a mutable ref object and creates a {current:… The only difference with the} object is that useRef returns the same REF object every time it renders, which is unique throughout the life of the component.
  • UseRef can store any variable value. This is similar to the way instance fields are used in class. Conclusion:
  • UseRef can store data that does not need to cause a page to be re-rendered.
  • If you deliberately want to read/latest/state from some asynchronous callback, you can use a ref to save it, modify it, and read from it.

5. useReducer

const [state, dispatch] = useReducer(reducer, initialState);
Copy the code

Reducer is a pure function that can transform state from one process to another only through actions; UseReducer is a process that passes (state,action) => newState, just like redux. Data flow: Dispatch (action) => Reducer Update state => Return the updated state

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case'decrement:return {count: state.count - 1};
    default:
      throw new Error();
  }
}
function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={()= > dispatch({type: 'decrement'})}>-</button>
      <button onClick={()= > dispatch({type: 'increment'})}>+</button>
    </>
  );
}
Copy the code

The official recommendation for the following scenarios requires useReducer better:

  • The state logic is complex and contains multiple child values, which can be processed centrally.
  • The next state depends on the previous state.
  • Want to build automated test cases more consistently.
  • Using useReducer can also optimize the performance of components that trigger deep updates if you want to change some of the state of child components at a deeper level, because you can pass dispatches to child components instead of callbacks.
  • Using reducer helps separate reads from writes. DEMO6
const fetchReducer = (state, action) = > {
  switch (action.type) {
    case"FETCH_INIT": return { ... state, loading: true, error: false }; Case "FETCH_SUCCESS" : return {... state, loading: false, error: false, data: action.payload }; case "FETCH_FAIL": return {... state, loading: false, error: true }; default: throw new Error(); }}; function Demo6() { const [state, dispatch] = useReducer(fetchReducer, { loading: false, error: false, msg: "", data: {} }); const getData = useCallback(async () => { try { dispatch({ type: "FETCH_INIT"}); const response = await fetch( "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
      );
      const res = await response.json();

      if (res.code) {
        dispatch({ type: "FETCH_SUCCESS".payload: res.data });
      } else {
        dispatch({ type: “FETCH_FAIL”, payload: res.msg }); }}catch (error) {
      dispatch({ type: “FETCH_FAIL”, payload: error }); }} []); useEffect(() = > {
    getData();
  }, [getData]);

  return (
    <Loading loading={state.loading}>
      <p>OpenCode: {state.data.opencode}</p>
    </Loading>
  );
}
Copy the code

Demo6useReducer handles a number of logic that can be implemented with useState, including loading, error, MSG, and data.

UseContext and useReducer simulate the Redux management state

import React, { useReducer, useContext } from"React";const ModalContext = React.createContext();

const visibleReducer = (state, action) = > {
  switch (action.type) {
    case"CREATE" :return{... state, ... action.payload };case "EDIT":
      return{... state, ... action.payload };default:
      returnstate; }};function Demo7() {
  const initModalVisible = {
    create: false.edit: false
  };
  const [state, dispatch] = useReducer(visibleReducer, initModalVisible);

  return (
    <ModalContext.Provider value={{ visibles: state.dispatch}} >
      <Demo7Child />
    </ModalContext.Provider>
  );
}
function Demo7Child() {
  return (
    <div>
      Demo7Child
      <Detail />
    </div>
  );
}
function Detail() {
  const { visibles, dispatch } = useContext(ModalContext);
  console.log("contextValue", visibles);
  return (
    <div>
      <p>create: {`${visibles.create}`}</p>
      <button
        onClick={()= >Dispatch ({type: "CREATE", payload: {CREATE: true}})} > Enable CREATE Modal</button>
    </div>
  );
}
export default Demo7;
Copy the code

The logic is clearly drawn out, and the values in the context value do not need to be passed through in the component; they need to be fetched immediately. DEMO7

Note that React ensures that the identity of the Dispatch function is stable and does not change when the component is re-rendered. This is why it is safe to omit Dispatch from useEffect or useCallback’s dependency list.

6. useCallback

Grammar:

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

Returns a Memoized callback function.

What problem does useCallback solve? Look at the DEMO8

import React, { useRef, useEffect, useState, useCallback } from"React";function Child({ event, data }) {
  console.log("child-render");
  / / the fifth edition
  useEffect(() = > {
    consoleThe log (" child - useEffect "); event(); }, [event]);return (
    <div>
      <p>child</p>{/ *<p>props-data: {data.data && data.data.openCode}</p>* /}<button onClick={event}>Call the parent event</button>
    </div>
  );
}

const set = new Set(a);function Demo8() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState({});

  / / the first edition
  // const handle = async () => {
  // const response = await fetch(
  // "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
  / /);
  // const res = await response.json();
  // console.log("handle", data);
  // setData(res);
  // };

  / / the second edition
  // const handle = useCallback(async () => {
  // const response = await fetch(
  / / "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
  / /);
  // const res = await response.json();
  / / the console log (" handle ", data);
  // setData(res);
  // });

  / / the third edition
  // const handle = useCallback(async () => {
  // const response = await fetch(
  / / "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
  / /);
  // const res = await response.json();
  // setData(res);
  / / the console log (" useCallback ", data);
  // // eslint-disable-next-line react-hooks/exhaustive-deps
  / /} []);

  // // Fourth Edition
  // const handle = useCallback(async () => {
  // const response = await fetch(
  / / "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
  / /);
  // const res = await response.json();
  // setData(res);
  / / the console. The log (" parent - useCallback ", data);
  // // eslint-disable-next-line react-hooks/exhaustive-deps
  / /} []);

  / / the fifth edition
  const handle = useCallback(async() = > {const response = await fetch(
      "https://www.mxnzp.com/api/lottery/common/latest?code=ssq"
    );
    const res = await response.json();
    setData(res);
    console.log("parent-useCallback", data);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [count]);
  set.add(handle);

  console.log (" parent - render = = = = > ", data);return (
    <div>
      <button
        onClick={e= > {
          setCount(count + 1);
        }}
      >
        count++
      </button>
      <p>set size: {set.size}</p>
      <p>count:{count}</p>
      <p>data: {data.data && data.data.openCode}</p>
      <p>-------------------------------</p>
      <Child event={handle} />
    </div>
  );
}
export default Demo8;
Copy the code

Conclusion:

  • First version: Every time render, Handle is a new function, and every time it gets the latest data.
  • The second version: wrap handle with useCallback. Every render, Handle is a new function and can get the latest data every time. It has the same effect as the first version, so it is not recommended to use it in this way.
  • If the second parameter deps, handle will be memoized, so each data is the data (closure) from the first memory.
  • Version 4: useCallback relies on count changes, and whenever useCallback changes, Handle is rememoized.
  • Version 5: Every time the count changes, the function passed to the child component is the latest, causing the child’s useEffect to be executed. Conclusion:
  • UseCallback will return oneCallback version of memoryChange only if one of the dependencies has changed.
  • This approach is useful when passing callbacks to optimized subcomponents that depend on reference equality to prevent unnecessary rendering.
  • Using the callback function passed as an argument, the render function changes each time, also causing the child component rerender, which useCallback can optimize.Question: How to optimize unnecessary rendering of child components?

7. useMemo

Const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]); Returns a Memoized value. As with useCallback, the memoized value is recalculated when the dependency changes. UseMemo differs from useCallback in that it allows you to apply Memoized to any value type (not just functions). DEMO9

import React, { useState, useMemo } from"React";function Demo9() {
  const [count, setCount] = useState(0);
  const handle = () = > {
    consoleLog (" handle ", count);return count;
  };

  const handle1 = useMemo(() = > {
    console.log("handle1", count);
    return count;
    // eslint-disable-next-line react-hooks/exhaustive-deps} []);const handle2 = useMemo(() = > {
    console.log (" handle2 ", count);// Large computation method
    return count;
  }, [count]);

  console.log("render-parent");

  return (
    <div>
      <p>
        demo9: {count}
        <button onClick={()= > setCount(count + 1)}>++count</button>
      </p>
      <p>-------------------</p>
      <Child handle={handle1} />
    </div>
  );
}

function Child({ handle }) {
  console.log("render-child");
  return (
    <div>
      <p>child</p>
      <p>props-data: {handle}</p>
    </div>
  );
}
export default Demo9;
Copy the code

Conclusion:

  • useMemoWill be inrender Before the execution.
  • If the dependency array is not provided, useMemo evaluates the new value each time it renders.
  • useMemoUsed to return to thememoize, prevent each renderLarge amount of calculationThe cost of that.
  • useuseMemoBe careful with optimization, because optimization itself involves computation,Most of the time, you don’t need to worry about optimizing unnecessary re-renders.

Other Hook

1. useImperativeHandle

// ref: the ref to be passed
CreateHandle: method that needs to be exposed to the parent.
/ / deps: rely on
useImperativeHandle(ref, createHandle, [deps])
Copy the code

UseImperativeHandle should be used together with the forwardRef. Look at the DEMO10

import React, {
  useRef,
  forwardRef,
  useImperativeHandle,
  useEffect,
  useState
} from "react";

const Child = forwardRef((props, ref) = > {
  const inputEl = useRef();
  const [value, setVal] = useState("");
  / / the first edition
  // useImperativeHandle(ref, () => {
  // console.log("useImperativeHandle");
  // return {
  // value,
  // focus: () => inputEl.current.focus()
  / /};
  // });

  / / the second edition
  useImperativeHandle(
    ref,
    () = > {
      console.log (" useImperativeHandle"); return { value, focus: () => inputEl.current.focus() }; }, // eslint-disable-next-line react-hooks/exhaustive-deps [] ); return (  setVal(e.target.value)} value={value} {... props} /> ); }); function Demo10() { const inputEl = useRef(null); UseEffect (() => {console.log(" parent-useeffect ", inputel.current); inputEl.current.focus(); } []); function click() { console.log("click:", inputEl.current); inputEl.current.focus(); } the console. The log (" Demo10 ", inputEl. Current); return ( 
      
); } export default Demo10;
Copy the code

Conclusion:

  • useImperativeHandleExecutes after the current component render.
  • First edition: Without DEps, whenever rerender,useImperativeHandle Is executed and gets the latest value in state, as is the method passed in by the parent component.
  • Edition 2: Dependence[]Whenever rerender,useImperativeHandle Does not execute and does not update to the parent component.
  • Version 3: Rely on the state value passed in[value]To achieve the desired effect.

2. useDebugValue

Not commonly used, can only be seen in React Developer Tools. See the official portal for details.

DEMO11

function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(false); "FriendStatus: Online" useDebugValue(isOnline? "Online" : "Offline"); return isOnline; } function Demo11() { const isOnline = useFriendStatus(567); Return <div> Whether friends are online: {isOnline? }</div>; }Copy the code

3. useLayoutEffect

Rarely used, like useEffect, but it calls Effect synchronously after all DOM changes, as described in the official portal.


📢 finally

Welcome to 😀🍻🍻😀

Next up

Practice of Custom Hook in Project

Related reading:

  • When to useMemo and useCallback
  • thinking-in-react
  • React hooks: not magic, just arrays – Rudi Yardley – Medium
  • www.netlify.com/blog/2019/0…
  • Deep Understanding: How do React hooks work? – zhihu

Warm tips: ** more stretching, good for the body


React Hook series

  • React Hook Series 2: Some practices for customizing hooks
  • React Hook series (3) : Remember the precipitation of Hook in the Middle stage project

Guest officer, quality three even! 🤡 🤡 🤡