Introduction to the use of 10 Hooks in React

What are React Hooks?

React official website is like this: Hook is a new feature of React 16.8. It lets you use state and other React features without writing a class.

Completely optional. You can try out Hooks in some components without rewriting any existing code. But you don't have to learn or use Hooks now if you don't want to.

100% backward compatible Hooks contain no breaking changes.

Available now Hooks have been released in v16.8.0.

There are no plans to remove classes from React. You can read more about the progressive strategy for Hooks in the section at the bottom of this page.

Hooks don't affect your understanding of React concepts. On the contrary, Hooks provide a more direct API for known React concepts: props, state, context, refs, and lifecycle. As we'll see later, Hooks also provide a more powerful way to combine them.

If you don’t know enough about react, it is recommended to read the official document of react first, write a demo and then read the article, because I will mention some basic things of react without going into details.

Hooks currently provided by React

hook use
useState Set and change state, instead of original state and setState
useEffect Instead of the original lifecycle, a merged version of componentDidMount, componentDidUpdate and componentWillUnmount
useLayoutEffect Same as useEffect, but it calls effect synchronously
use Memo Control component update conditions, can be executed according to state change control methods, and optimize value transfer
useCallback useMemo optimizes the value transfer, usecallback optimizes the transfer method, whether to update
useRef Same as the previous ref, only more concise
useContext Contextual grandchildren and deeper component value passing
useReducer Instead of the reducer in the original redux, use it together with useContext
useDebugValue Display the label of the custom hook in the React developer tool for debugging.
useImperativeHandle Allows you to customize the instance value exposed to the parent component when using ref.

1.useState

import React from 'react';
import './App.css';
//通常的class写法,改变状态
class App extends React.Component {
  constructor(props){
    super(props)
    this.state = {
      hook:'react hook 是真的好用啊'
    }
  }
  changehook = () => {
    this.setState({
      hook:'我改变了react hook 的值'
    })
  }
  render () {
    const { hook } = this.state
    return(
         <header className="App-header">
          {hook}
          <button onClick={this.changehook}>
            改变hook
          </button>
        </header>
      )
  }
}
export  {App}
  
//函数式写法,改变状态
function App() {
//创建了一个叫hook的变量,sethook方法可以改变这个变量,初始值为‘react hook 是真的好用啊'
 const [hook, sethook] = useState("react hook 是真的好用啊");
  return ( 
    <header className="App-header">
      {hook}{/**这里的变量和方法也是可以直接使用的 */}
      <button onClick={() => sethook("我改变了react hook 的值")}>
        改变hook
      </button>
    </header>
  );
}
export  {App}
  
//箭头函数的函数写法,改变状态
export const App = props => {
  const [hook, sethook] = useState("react hook 是真的好用啊");
  return (
    <header className="App-header">
      {hook}
      <button onClick={() => sethook("我改变了react hook 的值")}>
        改变hook
      </button>
    </header>
  );

2.useEffect & useLayoutEffect

useEffect replaces the original life cycle, the merged version of componentDidMount, componentDidUpdate and componentWillUnmount
useEffect( ()=>{ return ()=>{ } } , [ ])

  • The first parameter is a function, which will be triggered by default when rendering and updating for the first time. By default, it comes with a return, and a function of return means that it can handle some things before it is destroyed.
  • The second parameter, the array [], when it is empty, means that it will only be executed once, and it will not be triggered when it is updated. What is the parameter in it, and useEffect will be executed when the parameter changes
    • useEffect can be used multiple times and executed in sequence
    • useLayoutEffect forces the execution of useeffect to be synchronous, and executes the function inside useLayoutEffect first
import React, { useState, useEffect, useLayoutEffect } from 'react';

//箭头函数的写法,改变状态
const UseEffect = (props) => {
 //创建了一个叫hook的变量,sethook方法可以改变这个变量,初始值为 react hook 是真的好用啊
 const [ hook, sethook ] = useState('react hook 是真的好用啊');
 const [ name ] = useState('baby张');
 return (
  <header className="UseEffect-header">
   <h3>UseEffect</h3>
   <Child hook={hook} name={name} />
   {/**上面的变量和下面方法也是可以直接使用的 */}
   <button onClick={() => sethook('我改变了react hook 的值' + new Date().getTime())}>改变hook</button>
  </header>
 );
};

const Child = (props) => {
 const [ newhook, setnewhook ] = useState(props.hook);
 //这样写可以代替以前的componentDidMount,第二个参数为空数组,表示该useEffect只执行一次
 useEffect(() => {
  console.log('first componentDidMount');
 }, []);

 //第二个参数,数组里是hook,当hook变化时,useEffect会触发,当hook变化时,先销毁再执行第一个函数。
 useEffect(
  () => {
   setnewhook(props.hook + '222222222');
   console.log('useEffect');
   return () => {
    console.log('componentWillUnmount ');
   };
  },
  [ props.hook ]
 );

 //useLayoutEffect 强制useeffect的执行为同步,并且先执行useLayoutEffect内部的函数
 useLayoutEffect(
  () => {
   console.log('useLayoutEffect');
   return () => {
    console.log('useLayoutEffect componentWillUnmount');
   };
  },
  [ props.hook ]
 );
  
 return (
  <div>
   <p>{props.name}</p>
   {newhook}
  </div>
 );
};


export default UseEffect;

 

3. use Memo

It can be used to optimize the rendering of sub-components, or monitor the status changes of sub-components to handle events. This was difficult to do in the past, because shouldComponentUpdate can monitor whether there are changes, but there is no way to control other external methods. Can return true and false, and componentDidUpdate can only be executed after updating, so it is not easy to do something before rendering.

import React, { useState, useMemo } from 'react';
  
const Child = ({ age, name, children }) => {
//在不用useMemo做处理的时候,只要父组件状态改变了,子组件都会渲染一次,用了useMemo可以监听某个状态name,当name变化时候执行useMemo里第一个函数
  console.log(age, name, children, '11111111');
  function namechange() {
    console.log(age, name, children, '22222222');
    return name + 'change';
  }
//useMemo有两个参数,和useEffect一样,第一个参数是函数,第二个参数是个数组,用来监听某个状态不变化
 const changedname = useMemo(() => namechange(), [ name ]);
 return (
  <div style={
   
   { border: '1px solid' }}>
   <p>children:{children}</p>
   <p>name:{name}</p>
   <p>changed:{changedname}</p>
   <p>age:{age}</p>
  </div>
 );
};
  
const UseMemo = () => {
    //useState 设置名字和年龄,并用2两个按钮改变他们,传给Child组件
 const [ name, setname ] = useState('baby张'); 
 const [ age, setage ] = useState(18);
 return (
  <div>
   <button
    onClick={() => {
     setname('baby张' + new Date().getTime()); 
    }}
   >
    改名字
   </button>
   <button
    onClick={() => {
     setage('年龄' + new Date().getTime());
    }}
   >
    改年龄
   </button>
   <p>
    UseMemo {name}:{age}
   </p>
   <Child age={age} name={name}>
    {name}的children
   </Child>
  </div>
 );
};
  
export default UseMemo;

4.useCallback

Simply put, it returns a function that will only be updated (returning a new function) when the dependencies change.

    // 父组件,给子组件传递name和changeName方法
    const Parent = () => {
      const [count, setCount] = useState(1);
      const [name, setName] = useState("bbz");
      const addCount = () => {
        setCount(count + 1);
      };
      const changeName = useCallback((n) => {
        setName(n);
      }, []);
      return (
        <>
          <div onClick={addCount}>计数: {count}</div>
          <Child name={name} changeName={changeName} />
        </>
      );
    };
    
    // 子组件
    const Child = ({ name, changeName }) => {
      console.log("child start---");
      return (
        <div
          onClick={() => {
            changeName("bobozai");
          }}
        >
          child comps: {name}
        </div>
      );
    };
    
    // 如果不使用useCallback,则点击父组件计数的同时,子组件会console进行渲染,
    // 因为更新count时父组件会重新渲染,导致重新生成了一个changeName函数,
    // 所以子组件的props变了,导致子组件会重新渲染
    // 而对changeName函数用useCallback进行包裹,则对函数进行缓存不会重新生成

Code Online:  Code Sandbox

import React, { useState, useCallback } from 'react';
import Button from './Button';

export default function App() {
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
  const [count3, setCount3] = useState(0);

  const handleClickButton1 = () => {
    setCount1(count1 + 1);
  };

  const handleClickButton2 = useCallback(() => {
    setCount2(count2 + 1);
  }, [count2]);

  return (
    <div>
      <div>
        <Button onClickButton={handleClickButton1}>Button1</Button>
      </div>
      <div>
        <Button onClickButton={handleClickButton2}>Button2</Button>
      </div>
      <div>
        <Button
          onClickButton={() => {
            setCount3(count3 + 1);
          }}
        >
          Button3
        </Button>
      </div>
    </div>
  );
}
// Button.jsx
import React from 'react';

const Button = ({ onClickButton, children }) => {
  return (
    <>
      <button onClick={onClickButton}>{children}</button>
      <span>{Math.random()}</span>
    </>
  );
};

export default React.memo(Button);

In the case, you can click several buttons in the Demo to view the effect:

  • When Button1 is clicked, only the content behind Button1 and Button3 will be updated;
  • Clicking Button2 will update the content behind the three buttons;
  • When Button3 is clicked, only the content behind Button1 and Button3 will be updated.

After careful consideration of the above effects, it can be found that only the useCallbackoptimized Button2 will change when it is clicked on itself, and the other two will change as long as the parent component is updated (here Button1 and Button3 are actually the same, nothing more than a function change place to write). Let's take a closer look at the specific optimization logic.

Here you may notice the React.memo method of the Button component. In this method, a shallow comparison of props will be made. If the props have not changed, the component will not be re-rendered.

const [count1, setCount1] = useState(0);
// ...
const handleClickButton1 = () => {
  setCount1(count1 + 1);
};
// ...
return <Button onClickButton={handleClickButton1}>Button1</Button>

Looking back at the above Buttoncomponents all need a onClickButtonprops , although the component is useful React.memofor optimization, but what we declare handleClickButton1is to directly define a method, which will cause as long as the parent component is re-rendered (status or props update) will cause A new method is declared here. Although the new method and the old method have the same length, they are still two different objects. After comparison, it is found that the React.memoobject props have changed, and they are re-rendered.

const handleClickButton2 = useCallback(() => {
  setCount2(count2 + 1);
}, [count2]);

In the above code, our method uses useCallback to wrap a layer, and a [count2]variable , here useCallback will count2decide whether to return a new function according to whether or not changes, and the internal scope of the function will also be updated accordingly.

Since our method only relies on count2this variable, and it will count2 only be updated after Button2 is clicked handleClickButton2, it causes us to click Button1 without re-rendering the content of Button2.

5.useRef

ref is almost the same as before, useRef creates – binds – uses, three steps, see the code and notes for details

import React, { useState, useRef } from 'react';
  
const UseRef = () => {
 //这里useState绑定个input,关联一个状态name
 const [ name, setname ] = useState('baby张');
 const refvalue = useRef(null);// 先创建一个空的useRef
 function addRef() {
  refvalue.current.value = name;   //点击按钮时候给这个ref赋值
  // refvalue.current = name  //这样写时,即使ref没有绑定在dom上,值依然会存在创建的ref上,并且可以使用它
  console.log(refvalue.current.value);
 }
 return (
  <div>
   <input defaultValue={name}
     onChange={(e) => {
       setname(e.target.value);
     }
   />
   <button onClick={addRef}>给下面插入名字</button>
   <p>给我个UseRef名字:</p>
   <input ref={refvalue} />
  </div>
 );
};

export default UseRef;

6.useContext

Friends who have used context before can understand it at a glance. The basic usage of useContext is similar to the previous context. There are detailed comments in the code, creating, passing values, and using

import React, { useState, useContext, createContext } from 'react';
  
const ContextName = createContext();
//这里为了方便写博客,爷爷孙子组件都写在一个文件里,正常需要在爷爷组件和孙子组件挨个引入创建的Context
  
const UseContext = () => {
 //这里useState创建一个状态,并按钮控制变化
 const [ name, setname ] = useState('baby张');
 return (
  <div>
   <h3>UseContext 爷爷</h3>
   <button
    onClick={() => {
     setname('baby张' + new Date().getTime());
    }}
   >
    改变名字
   </button>
   {/**这里跟context用法一样,需要provider向子组件传递value值,value不一定是一个参数 */}}
   <ContextName.Provider value={
   
   { name: name, age: 18 }}>
    {/**需要用到变量的子组件一定要写在provider中间,才能实现共享 */}
    <Child />
   </ContextName.Provider>
  </div>
 );
};
  
const Child = () => {
 //创建一个儿子组件,里面引入孙子组件
 return (
  <div style={
   
   { border: '1px solid' }}>
   Child 儿子
   <ChildChild />
  </div>
 );
};
  
const ChildChild = () => {
 //创建孙子组件,接受爷爷组件的状态,用useContext,获取到爷爷组件创建的ContextName的value值
 let childname = useContext(ContextName);
 return (
  <div style={
   
   { border: '1px solid' }}>
   ChildChild 孙子
   <p>
    {childname.name}:{childname.age}
   </p>
  </div>
 );
};
  
export default UseContext

7.useReducer

The usereducer here will return the state and dispatch, pass it to the subcomponent through the context, and then directly call the state or trigger the reducer. We often use useReducer together with useContext createContext to simulate the value transfer and reassignment operations of reudx.

import React, { useState, useReducer, useContext, createContext } from 'react';
  
//初始化stroe的类型、初始化值、创建reducer
const ADD_COUNTER = 'ADD_COUNTER';
const initReducer = {
 count: 0
};
//正常的reducer编写
function reducer(state, action) {
 switch (action.type) {
  case ADD_COUNTER:
   return { ...state, count: state.count + 1 };
  default:
   return state;
 }
}
  
const CountContext = createContext();
//上面这一段,初始化state和reducer创建context,可以单独写一个文件,这里为了方便理解,放一个文件里写了
  
const UseReducer = () => {
 const [ name, setname ] = useState('baby张');
 //父组件里使用useReducer,第一个参数是reducer函数,第二个参数是state,返回的是state和dispash
 const [ state, dispatch ] = useReducer(reducer, initReducer);
 return (
  <div>
   UseReducer
   {/* 在这里通过context,讲reducer和state传递给子组件*/}
   <CountContext.Provider value={
   
   { state, dispatch, name, setname }}>
    <Child />
   </CountContext.Provider>
  </div>
 );
};
  
const Child = () => {
 //跟正常的接受context一样,接受父组件的值,通过事件等方式触发reducer,实现redux效果
 const { state, dispatch, name, setname } = useContext(CountContext);
 function handleclick(count) {
  dispatch({ type: ADD_COUNTER, count: 17 });
  setname(count % 2 == 0 ? 'babybrother' : 'baby张');
 }
 return (
  <div>
   <p>
    {name}今年{state.count}岁
   </p>
   <button onClick={() => handleclick(state.count)}>长大了</button>
  </div>
 );
};
  
export default UseReducer;

Guess you like

Origin blog.csdn.net/qq_38261819/article/details/126029888