react中Hooks的理解和用法小结

一、Hooks是什么? Hook 是 React 16 8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

一、Hooks是什么?

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

至于为什么引入hook,官方给出的动机是解决长时间使用和维护react过程中常遇到的问题,例如:

  • 难以重用和共享组件中的与状态相关的逻辑
  • 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面
  • 类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题
  • 由于业务变动,函数组件不得不改为类组件等等

在以前,函数组件也被称为无状态的组件,只负责渲染的一些工作

因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理

二、有哪些?具体用法

1、useState

作用:为函数式组件提供动态属性

语法:let [默认数据变量, 修改数据的方法] = useState('默认数据')

  • 修改 基本数据类型:修改这个数据的方法(最新的数据)

  • 修改复杂数据类型 数组:修改数据方法(()=>{return [ ...新的数据]})

  • 修改复杂数据类型 对象:修改数据方(()=>{...原数据state, 更新的属性数据})

// 引入useState
import { useState } from "react";
// hooks 为函数式组件提供一个属性
// 作用 => 为函数式组件提供动态数据
// 语法 => let [默认数据变量,修改这个数据的方法] = useState('默认数据')
// // 1. 修改的是 基本数据类型
// function App(){
//   // 这里组件有一个动态的数据
//   // let salary=1800;
//   // 动态的数据 =》 useState
//   let [salary,setSalary]=useState(1800);
//   const changeSalary=()=>{
//     // 让salary => 改变值 => useState 处理的是基本数据类型
//     // setSalary(最新的数据) => salary 是最新的数据
//     setSalary(2000)
//   }
//   return (
//     <div>
//         <h2>我的工资{salary}</h2>
//         <button onClick={()=>changeSalary()}>修改数据</button>
//     </div>
//   );
// }
// // 2.修改复杂数据类型之 数组
// function App() {
//   // 动态数据 => 数组
//   let [list, setList] = useState(["", "⚽", ""]);
//   const addItem = () => {
//     // 添加数据
//     let item = "";
//     setList((state) => {
//       state.push(item);
//       return [...state];
//     });
//   };
//   const deleteItem =(index)=>{
//   // 修改数据 list => 调用 他的修改数据方法 setList
//       // console.log(list,index);
//       // list.splice(index,1);
//       // 处理复杂的数据类型   => (处理方法)
//       // 处理方法 特点
//       // 第一个参数 就是默认数据
//       // 这个处理方法的返回值就是 最新的数据
//       setList((state)=>{
//         state.splice(index,1);  // 获取到原理的数据 => 根据索引删除
//         console.log(state);
//         return([...state]); // 改变引用地址
//       })
//   }
//   return (
//     <div>
//       {list.map((item, index) => {
//         return <div key={index}>{item} <button onClick={()=>deleteItem(index)}>删除</button></div>;
//       })}
//       <button onClick={() => addItem()}>添加</button>
//     </div>
//   );
// }
// 3 修改复杂数据类型之 对象
function App() {
  // 定义一个动他的数据 小明信息
  let [obj, setObj] = useState({ name: "小明", salary: 1800 });
  const changeSalary=()=>{
    setObj(state=>{
      // 1 获取到原来的数据属性,在添加我们需要更新的属性
      return {...state,salary:state.salary+200}
    })
  }
  return (
    <div>
      <h2>
        {obj.name}的月薪{obj.salary} 每日笑哈哈
        <button onClick={()=>changeSalary()}>修改数据 学习后</button>
      </h2>
    </div>
  );
}
export default App;
/* 
    总结 函数式组件的动态数据的处理
    1 使用方式 useState
        语法 let [默认数据,修改数据的方法]=useState(默认值)
        1 基本数据类型 =>   修改数据方法(新的数据)
        2 数组  => 修改数据的方法(()=>{
          retrun[...新的数据]
        })
        3 对象  => 修改数据方法(处理方法中自己合并旧的数据) => 合并原理的数据
*/

2、useEffect 副作用

Effect Hook 可以让你在函数组件中执行副作用操作

作用:1. 函数式组件的生命周期 2. 侦听器

函数式组件的生命周期

  • 组件加载完毕:语法: useEffect(处理函数, 依赖项) ; 如果依赖项为空数组,useEffect( ()=>{ }, [ ]) 组件加载完毕,相当于mounted,执行一次,一般用来做dom操作,和发送请求

  • 组件更新完毕:语法:useEffect(()=>{ }) ;如果没有依赖项,这个处理函数,只要这个函数(组件)重新执行,处理函数就会重新执行(一般不使用)

  • 组件摧毁:语法:useEffect(()=>{return()=>{ }};这个useEffect 处理函数式的返回值,并且是处理函数的返回值 是一个方法;用于 清除这个组件中全局定义的一些方法

import { useEffect,useState } from "react";
 // useEffect => 副作用
    // 1 当组件的生命周期使用
        // 写法 1 useEffect(()=>{}) => 1.加载完毕 2.更新完毕 3.组件摧毁
            // 1 加载完毕 => dom 操作,发动网络请求 获取到后端的数据
            // 2 组件更新
// 想要的组件的加载 和更新  =》 在工作中不用
// function App(){
//    let [salary,setSalary]=useState(1800); 
//     useEffect(()=>{
//         console.log(document.getElementById('h2'));
//         console.log('组件在浏览器上加载完毕,组件更新');
//     })
//    const addSaraly=()=>{
//     setSalary(salary+200)
//    }
//     return(
//         <div>
//             <h2 id='h2'>useEffect 作用</h2>
//             <h3>{salary}</h3>
//             <button onClick={()=>addSaraly()}>++</button>
//         </div>
//     )
// }
// // 2 发现我们需要这个副作用, 我们的生命周期(组件加载完毕) 执行 mounted
// // 语法 useEffect(()=>{},依赖项)
// // // 如果这个依赖项为空的数组 useEffect(()=>{},[]) => 就相对于vue, 执行一次
// function App(){
//     let [salary,setSalary]=useState(1800); 
//     console.log('组件created f')    // 还是会重新创建组件
//      useEffect(()=>{
//          console.log(document.getElementById('h2'));
//          console.log('组件加载完毕');
//      },[])
//      // useEffect(处理函数,依赖项)
//      // 处理函数和依赖项之间的关系
//      // 1 如果没有依赖项,这个处理函数,只要这个函数重新执行,处理函数就会重新执行
//      // 2 如果有依赖项,但是这个依赖项为空数组,那么这个处理函数只会执行一次
//     const addSaraly=()=>{
//      setSalary(salary+200)
//     }
//      return(
//          <div>
//              <h2 id='h2'>useEffect 作用</h2>
//              <h3>{salary}</h3>
//              <button onClick={()=>addSaraly()}>++</button>
//          </div>
//      )
//  }
//  function Children(){
//      return(
//          <div>
//          </div>
//      )    
//  }
// 2 相当于 组件的摧毁
function App(){
    let [salary,setSalary]=useState(1800); 
    console.log('组件created f')    // 还是会重新创建组件
    const addSaraly=()=>{
     setSalary(salary+200)
    }
     return(
         <div>
             <h2 id='h2'>useEffect 作用</h2>
             <h3>{salary}</h3>
             {
               salary==2000?<Children></Children>:<h4>444</h4>  
             }
             <button onClick={()=>addSaraly()}>++</button>
         </div>
     )
 }
 function Children(){
    // 组件的摧毁的生命周期
    // 写法 => 是这个useEffect 处理函数式的返回值,并且是处理函数的返回值 是一个方法
    let timer=setInterval(()=>{
        console.log(1)
    },1000)
    useEffect(()=>{
        return()=>{
            console.log('组件摧毁了');
            clearInterval(timer)    // 清除全局
        }
    })
     return(
         <div>
                Children 
         </div>
     )    
 }
 /* 
    总结
        1 函数式组件的生命周期 
        2 另外的3个 是通过 react hooks 提供
            2.1 useEffect(()=>{},[])    =》 加载完毕 => 发送请求 dom操作
            2.2 组件的更新 => 工作中不用
            2.3 useEffect(()=>{return 方法})  =》 组件的摧毁 =》 清除这个组件全局定义的一个方法
 */
export default App;

组件中的侦听器

1. 作用:侦听器,相当于vue中的watch

  • 语法:useEffect(()=>{},[监听的数据1,监听的数据2]) ;useEffect(()=>{},[依赖的数据源]),相当于立即执行的侦听器,监听的函数发生改变,处理函数就会改变

  • useEffectb本质就是一个宏任务

import { useEffect,useState } from "react";
// useEffect =》 相当于vue中的watch
function App(){
    let [salary,setSalary]=useState(1800);
    let [name,setName]=useState('小红'); 
    console.log('组件created f','还会不会重新创建组件?')    // 还会不会重新创建组件?
   // watch => 立即执行
    useEffect(()=>{
        console.log(salary)
    },[salary])
    const addSaraly=()=>{
     setSalary(salary+200)
    }
     return(
         <div>
             <h2 id='h2'>useEffect 作用</h2>
             <h3>{salary}</h3>
            <button onClick={()=>addSaraly()}>++</button>
            <h3>{name}</h3>
            <button onClick={()=>setName('小明')}>修改</button>
         </div>
     )
 }
 // 总结: useEffect(()=>{},[依赖的数据源]),相对于立即执行的侦听器
 // useEffect 本质就是一个宏任务
export default App;

3、useContext和React.createContext

useContext

  • 作用:全局提供数据,用来做全局设置

  • 用法: useContext 必须要和 React.createContext 一起使用

    创建一个全局数据:let Context=React.createContext() // 返回一个对象:有一个属性provider 是提供数据的

    在父子组件中提供数据:<Context.Provider value={所提供的数据} ></Context.Provider>

    在子组件中获取父组件提供的数据:let data=useContext(Context) // 相当于inject,data就是在父组件中获取到的数据

// useContext => 提供上下文 =》 本质=》全局变量
import React,{ useContext } from "react";
// 在最外层提供数据, 在里面可以获取到数据
// 业务中的使用 => 全局设置主体,字体 dengdeng
// 使用:必须 提供数据provideer =》 React.createContext()
let Context =React.createContext()  // 返回一个对象 => 有一个属性 provider => 提供数据
console.log(Context)
let theme={
    background:'red'
};
function App(){
     return(
        // 在父级组件中提供数据
        <Context.Provider value={theme}>
         <div>
            <Children></Children>
         </div>
         </Context.Provider>
     )
 }
 // 创建子组件
 function Children(){
    // 在子组件中需要获取到父级组件提供的数据
    let datas=useContext(Context);  // inject()
    console.log(datas)  // {background: 'red'}
     return(
         <div style={{background:datas.background}}>
            Children
         </div>
     )    
 }
// 总结 useContext =》 获取到 React.createContext() 提供的数据
export default App;

4、useReducer

  • 用法:和useState 一样,都是用来处理动态数据

  • 作用:就是对动态的数据的行为(这个动态数据修改的方法)进行约束

  • 语法:语法: let [默认数据,dispatch] = useReducer(reducer,默认数)

  • 核心:

    retucer:reducer相当于vuex中的mutations,作用:定义行为(方法),修改动态数据

    reducer是一个函数

    这个函数有两个参数, 参数1 :数据 参数2: 行为

    这个函数的返回值,就是最新的数据

    只能处理同步函数

    用来触发reducer行为,dispatch({actions}) => 这个actions是一个对象

import {useState, useReducer } from 'react'
// useReducer
// 用法 和useState 一样,都是用来处理动态数据
// 作用 => 就是对动态的数据的行为(这个动态数据修改的方法)进行约束
// 语法 => let [默认数据,触发reducer行为]=useReducer(reducer,默认数据)
// reducer => 相当于vuex中 mutations
// 作用 => 定义行为(方法) => 修改动态数据
// reducer 是什么
// 1 它是一个函数
// 2 这个函数有两个参数, 参数1 就是你的数据      参数2 行为
// 3 这个函数返回值 就是你最新的数据
// 4 只能处理同步问题
// 定义reducer
function reducer(state,actions){
    // 定义你处理逻辑 => add reduce
    switch(actions.type){
        case 'add':
            console.log(state);  // 默认参数 
            console.log(actions);    // 行为
            return state+actions.money
        case 'reduce':
            return state-200;
        default :
        return state;
    }
}
function App(){
    let [money,setMoney]=useState(1800);
    let [moneys,dispatch]=useReducer(reducer,1000)
    const changeM=()=>{
        setMoney('椅子')
    }
    return(
        <div>
            <h2>{money}</h2>
            <button onClick={()=>changeM()}>操作money</button>
            <h2>{moneys}</h2>
            {/* 触发reducer => dispatch 触发reduce行为
                dispatch(就是reducer actions)   => 这个actions 是一个对象   传递对象
            */}
            <button onClick={()=>dispatch({type:'add',money:300})}>add</button>
        </div>
    )
}
export default App
/* 
    总结:
    userReducer  核心
    reducer => 用来定义处理动态数据的行为的
    dispatch => 触发 reducer 中的行为
*/

5、useRef

  • 作用:获取到元素的真实dom

  • 语法:let dom=useRef(null) ; (此时dom=null);在标签元素上<div ref={dom}></div>; (dom就可以获取到真实的dom)

  • 组件的创建流程:

    组件创建完毕:初始化这个组件的属性

    再将这个组件的模板语法(jsx)变为vnode(1. 如果有事件就集中处理,2. 如果又属性 就进行绑定)

    再将这个组件变为真实dom

    做一些dom操作

import {useRef,useEffect} from 'react'
// useRef
// 作用 :获取到元素的真实dom
// 在created 生命周期中进行定义useRef
// 用法 let dom=useRef(null)
function App(){
    let dom =useRef(null)   // null
    let datas=100;
    useEffect(()=>{
        console.log(dom);// 获取到真实的dom
        console.log(document.getElementById('h2s'));
    },[])
    return(
        <div>
            <h2 id='h2s' data-id={datas} ref={dom}>dom</h2>
        </div>
    )
}
/* 
    这个组件在创建的流程
    1 组件创建完毕  => 初始化 这个组件的属性
    2 将这个组件 模板语法(jsx) => 变成vnode( 1 如果有事件 集中处理,2 有属性 进行绑定) =》 再将这个vnode 变成真实dom
    3 做一些dom操作
*/
export default App;

6、useLayoutEffect

  • 作用:

    监听器:当监听的数据 发生改变之前就执行这个处理方法

    生命周期:语法:和useEffect一样 useLayout(处理函数,依赖项)

    组件更新之前:没有依赖项,只要组件更新,函数就会在组件更新前更新

    挂载完成之前:依赖项为空数组,相当于mounted,执行一次

    组件摧毁之前:useEffect(()=>return 方法) 返回的方法在组件摧毁前执行

    useLayoutEffect:底层代码是 微任务

import {useEffect,useLayoutEffect,useState} from 'react'
function App(){
    // useLayoutEffect
    // 写法 和useEffect 一样的
  /* 
    useLayoutEffect 作用
        1 生命周期
            浏览器加载之前 ,更新之前 , 摧毁之前
        2 监听
            当这个数据 发生改变之前就是执行这个处理方法
  */
    let [age,setAge]=useState(100);
    // useEffect(()=>{
    //     console.log('加载完毕 useEffect');
    // },[])
    // useLayoutEffect(()=>{
    //     console.log('浏览器生成dom 之前 useEffect')
    // },[])
   useEffect(()=>{
        console.log('加载完毕 useEffect');
    },[age])
    useLayoutEffect(()=>{
        console.log(age)
        console.log('浏览器生成dom 之前 useEffect')
    },[age])
    return(
        <div>
           {age}
           <button onClick={()=>setAge(age+1)}>++</button>
        </div>
    )
}
export default App;
// useLayoutEffect =》 底层代码是 微任务

7、useMemo

作用:1. 缓存组件 2. 缓存变量

  • 缓存组件:

    语法:let 组件= React.memo(需要缓存的组件)

    <组件></组件>

    特点:缓存组件 如果这个组件的属性没有变化就不会创建,反之就会重新创建

react组件更新机制:组件的重新创建,只要数据发生改变,组件就会重新创建,会造成性能问题

组件的渲染:先渲染父组件,在渲染子组件

问题1:在子组件中更新数据,如果没有将子组件进行模块化划分,那么父组件也会重新创建,会导致性能问题

解决方法:

  • 模块化开发:组件的模块化划分,将子组件进行抽离(一个功能一个模块),此时:(子组件数据更新,该子组件会重新创建,其父组件与兄弟组件不会重新创建)

问题2:如果我们在父组件中更新数据 => 会重新渲染组件 => 这个父组件的嵌套组件(子组件),也会被重新渲染 => 会导致性能问题

解决方法:

1. 缓存组件:React.mome(需要被缓存的组件); 缓存组件,不管父组件是否重新创建,这个子组件创建一次,如果子组件的数据没有改变,就不需要重新创建

// react 更新机制 => 组件重新创建
import React, { useState } from "react";
// // 组件模块化划分
// function App() {
//   console.log("father 组件重新创建了");
//   return (
//     <div>
//       {/* 模块化划分 */}
//       <Children2></Children2>
//       <Children1></Children1>
//     </div>
//   );
// }
// function Children2() {
//   console.log("children2222222222 组件重新创建");
//   let [salary, setSalary] = useState(1800);
//   const changeSalary = () => {
//     setSalary(salary + 200);
//   };
//   return (
//     <div>
//       月入{salary}
//       <button onClick={() => changeSalary()}>更新数据+200</button>
//     </div>
//   );
// }
// function Children1() {
//   console.log("children111111 组件重新创建");
//   return <div>children</div>;
// }
function App() {
  console.log("father 组件重新创建了");
  let [salary, setSalary] = useState(1800);
  const changeSalary = () => {
    setSalary(salary + 200);
  };
  return (
    <div>
      月入{salary}
      <button onClick={() => changeSalary()}>更新数据+200</button>
      {/* 
        不管父组件是否重新创建,这个子组件创建一次,如果子组件的数据没有改变,就不需要重新创建
      */}
      <Keepchildren1></Keepchildren1>
    </div>
  );
}
/* 
    React.memo()=> 缓存组件
    语法: React.memo(需要缓存的组件) => 缓存组件
    特点:
        缓存组件  如果这个组件属性值没有变化就不会创建,反之,就会重新创建
*/
function Children1() {
  console.log("children111111 组件重新创建");
  return (<div>children</div>)
}
let Keepchildren1=React.memo(Children1)
export default App;
// 1 组件的渲染 => 先渲染父组件,再渲染子组件
// 2 如果我们在父组件中更新数据 => 会重新渲染组件 => 这个父组件的嵌套组件(子组件),也会被重新渲染   => 会导致性能问题
// 解决方法(子组件数据更新,该子组件会重新创建,其父组件与兄弟组件不会重新创建)
    // 1 模块化开发 => 一个功能一个模块
  • 缓存变量

    1. 作用:缓存变量

    2. 语法:let 缓存变量 = useMemo( ()=>{ return 缓存数据},[监听的数据源] ); // 当依赖的数据发生改变的时候才会更新

// react 更新机制 => 组件重新创建
import React, { useState,useMemo } from "react";
// useMemo  作用: 缓存变量
// 用法: let 缓存变量 = useMemo(()=>{},[监听的数据源])    // 当依赖的数据发生改变的时候才会更新
function App() {
  console.log("father 组件重新创建了");
  let [salary, setSalary] = useState(1800);
//   let name="张三";      // 这个数据写死 => 不用更新
let Kname=useMemo(()=>{
    console.log('更新');
    return '张三';  // 缓存数据  当依赖的数据发生改变的时候才会更新
},[salary])
  const changeSalary = () => {
    setSalary(salary + 200);
  };
  return (
    <div>
      <h2>{Kname}</h2>
      月入{salary}
      <button onClick={() => changeSalary()}>更新数据+200</button>
    </div>
  );
}
 export default App;

8、useCallback

  • 作用:缓存方法

  • 语法:let 缓存方法 = useCallback( ()=>{} , []); // 当依赖的数据发生改变的时候才会更新

import React, { useState,useMemo, useCallback } from "react";
// useCallback  作用: 缓存方法
// 用法: let 缓存方法 = useCallback(()=>{},[])
// 当依赖的数据发生改变的时候才会更新
function App() {
  console.log("father 组件重新创建了");
  let [salary, setSalary] = useState(1800);
  const getdata=()=>{
    console.log(1200);
  }
let Keepgetdata=useCallback(()=>{
    return getdata();
},[salary])
  const changeSalary = () => {
    setSalary(salary + 200);
  };
  return (
    <div>
      月入{salary}
      <button onClick={() => changeSalary()}>更新数据+200</button>
      <button onClick={()=>Keepgetdata()}>缓存方法</button>
    </div>
  );
}
 export default App;

9、自定义hooks

  • 概念:

    它是一个方法

    这个方法是以use 开头的

    在这个方法中可以使用 react 提供的api

到此这篇关于react中Hooks的理解和用法的文章就介绍到这了,更多相关react中Hooks用法内容请搜索好代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持好代码网!

您可能有感兴趣的文章
React渲染机制超详细讲解

react后台系统最佳实践示例详解

react中的双向绑定你真的了解吗

React组件的应用介绍

React组件通信如何实现方式详解