React学习(八)——Hooks

学习目标:能够理解并能够使用hooks

目录

一、Hooks概念

1、注意点:

2、 Hooks解决了什么问题

二、useState——为函数组件提供状态(state)

1、使用案例

2、总结使用步骤

3、回调函数作为参数

4、组件的更新过程

5、使用规则

三、useEffect——为react函数组件提供副作用处理

1、函数副作用

2、案例

3、总结使用步骤:

4、依赖项控制执行时机

5、使用useEffct hook发送网络请求

6、清除副作用

四、阶段小练习

1、useWindowScroll

2、自动同步数据到本地localStorage

五、useRef——获取真实dom或组件实例

1、示例

2、效果

3、总结使用步骤

4、useRef 与 createRef

六、useContext ——在hooks下的context使用方式

1、示例

2、对比之前的


一、Hooks概念

Hooks的本质:一套能够使函数组件更强大,更灵活的“钩子”

React体系里组件分为 类组件 和 函数组件

        经过多年的实战,函数组件是一个更加匹配React的设计理念 UI = f(data),也更有利于逻辑拆分与重用的组件表达形式,而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks应运而生

1、注意点:

  • 有了hooks之后,为了兼容老版本,class类组件并没有被移除,俩者都可以使用
  • 有了hooks之后,不能在把函数成为无状态组件了,因为hooks为函数组件提供了状态
  • hooks只能在函数组件中使用

2、 Hooks解决了什么问题

(1)组件的逻辑复用
        在hooks出现之前,react先后尝试了 mixins混入,HOC高阶组件,render-props等模式,但是都有各自的问题,比如mixin的数据来源不清晰,高阶组件的嵌套问题等等

(2)class组件自身的问题
        class组件就像一个厚重的‘战舰’ 一样,大而全,提供了很多东西,有不可忽视的学习成本,比如各种生命周期,this指向问题等等,而我们更多时候需要的是一个轻快灵活的'快艇'

二、useState——为函数组件提供状态(state)

1、使用案例

(1)做一个计数器,当点击按钮时,+1

(2)点击按钮时,名字由空为有值

// 导入
import { useState } from "react";

function App(){
    // 解构:状态值 修改该状态的函数
    const [num,setNum] = useState(0)
    const [name,setName]=useState('')
    const handleClick = (num)=>{
        // 调用函数更改值
        setNum(num+1)
    }
    return(
            <>
         
            <div>
                {/* 渲染 */}
                <span>{num}</span>
                <button onClick={()=>handleClick(num)}>+</button>
            </div>
            
            <div>
                {/* 渲染 */}
                <span>{name}</span>
                {/* 改变值 */}
                <button onClick={()=>{setName('是一个开朗活泼聪明人见人爱的人')}}>点击看我是谁</button>
                
            </div>
            </>
    )
}

export default App;

2、总结使用步骤

  • 导入 useState 函数
  • 调用 useState 函数,并传入状态的初始值,并从useState函数的返回值中,解构赋值,拿到状态和修改状态的方法: const [num,setNum] = useState(0) 
  • 在JSX中展示状态
  • 调用修改状态的方法更新状态   setNum(num+1)  

3、回调函数作为参数

(1)使用场景

        如果初始 state 需要通过计算才能获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用

(2)语法

const [name, setName] = useState(()=>{   
  // 编写计算逻辑    return '计算之后的初始值'
})

4、组件的更新过程

(1)组件第一次渲染——从头开始执行该组件中的代码逻辑

  • 调用 useState(0) 将传入的参数作为状态初始值,即:0
  • 渲染组件,此时,获取到的状态 num值为: 0

(2)组件第二次渲染

  • 点击按钮,调用函数修改状态,因为状态发生改变,所以,该组件会重新渲染
  • 组件重新渲染:会再次执行该组件中的代码逻辑,再次调用 useState(),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1,因此再次渲染组件的组件获取到的状态 num值为:1

注意:useState 的初始值(参数)只会在组件第一次渲染时生效。也就是说,以后的每次渲染,useState 获取到都是最新的状态值,React 组件会记住每次最新的状态值

5、使用规则

(1)useState 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态

(2)react按照hooks的调用顺序识别每一个hook,因此不能嵌套在if/for/其它函数中,因为那个执行的顺序会变得不确定

(3) setNum(num+1)  传入的参数是新的值,会替换原来的值一定要使用新的状态替换旧的状态,不能直接修改旧的状态(如num++),尤其是引用类型

(4)可以通过开发者工具查看hooks的状态

三、useEffect——为react函数组件提供副作用处理

1、函数副作用

(1)什么是函数副作用

        对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用        

(2)常见的副作用

  • 数据请求 ajax发送
  • 手动修改dom
  • localstorage操作

2、案例

        在修改num数据之后,通过操作Dom,把num的值放到页面标题中

// 导入
import { useState,useEffect } from "react";

function App(){
    const [num,setNum] = useState(0)

    useEffect(()=>{
        // 传递一个函数作为参数,在函数里面定义副作用
        document.title=num
    })
    return(
            <>
                <button onClick={()=>{setNum(num+1)}}>+</button>
            </>
    )
}

export default App;

3、总结使用步骤:

  • 导入 useEffect 函数
  • 调用 useEffect 函数,并传入回调函数
  • 在回调函数中编写副作用处理(dom操作)
  • 修改数据状态(当我们通过修改状态更新组件时,副作用也会不断被执行)

4、依赖项控制执行时机

                        ——似乎有点像vue中的监听,不确定再看看

(1)不添加依赖项

  • 组件初始渲染
  • 组件更新 (不管是哪个状态引起的更新)——我们刚刚的例子也是,如果有其他的状态,改变的话,同样也是会重新渲染的,但这种做法是不对的
useEffect(()=>{
    console.log('副作用执行了')
})

(2) 添加空数组

        组件只在首次渲染时执行一次

useEffect(()=>{
	 console.log('副作用执行了')
},[])

(3)添加特定依赖项

        副作用函数在首次渲染时执行,在依赖项发生变化时重新执行(下面的例子中,只有count改变了才会执行,name改变时不会执行的)

function App() {  
    const [count, setCount] = useState(0)  
    const [name, setName] = useState('zs') 
    
    useEffect(() => {    
        console.log('副作用执行了')  
    }, [count])  
    
    return (    
        <>      
         <button onClick={() => { setCount(count + 1) }}>{count}</button>      
         <button onClick={() => { setName('cp') }}>{name}</button>    
        </>  
    )
}

(4)注意事项

        useEffect 回调函数中用到的数据(比如,count)就是依赖数据,就应该出现在依赖项数组中,如果不添加依赖项可能就会有bug出现

5、使用useEffct hook发送网络请求

        不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回

useEffect(async ()=>{    
    const res = await axios.get('http://geek.itheima.net/v1_0/channels')   
    console.log(res)
},[])

正确写法:

        内部单独定义一个函数,然后把这个函数包装成同步 

useEffect(()=>{   
    async function fetchData(){      
       const res = await axios.get('http://geek.itheima.net/v1_0/channels')                            console.log(res)   
    } 
},[])

6、清除副作用

如果想要清理副作用 可以在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑

注意执行时机为:

  • 组件卸载时自动执行——函数组件没有周期函数componentWillUnmount
  • 组件更新时,下一个useEffect副作用函数执行之前自动执行
import { useEffect, useState } from "react";

function Test(){
    useEffect(()=>{
        let timer=setInterval(()=>{
            console.log('定时器执行了')
        },1000)
        // 在这里清理
        return()=>{
            clearInterval(timer)
        }  
    },[])
    return(
        <div>
           happy
        </div>
    )
}

function App(){
    const [show,setShow] = useState(true)
    return(
            <div>
                 {show ? <Test/> : null}
                <button onClick={()=>{setShow(!show)}}>switch</button>
            </div>
    )
}

export default App;

四、阶段小练习

1、useWindowScroll

        要求自定义一个hook函数,实现通过解构的形式获得滚动到顶部的距离y

(1)在hooks文件夹下创建一个useWindowScroll.js文件,实现并导出:

import { useState } from "react"

export function useWindowScroll(){
    // y是一个数据,可以通过useState进行处理
    const [y,setY] = useState(0)
    //获取到数据后重新赋值
    window.addEventListener('scroll',()=>{
        const h = document.documentElement.scrollTop
        setY(h)
    })
    return [y]
}

(2)App.js 导入并使用

import {useWindowScroll} from './hooks/useWindowScroll'

function App(){
    const [y] = useWindowScroll()
    console.log(y)
    return(
        <>
          <div style={{height:'12000px'}}></div>
        </>
    )
}

export default App;

2、自动同步数据到本地localStorage

const [message, setMessage] = useLocalStorage(key,defaultValue)

  • message可以通过自定义传入默认初始值
  • 每次修改message数据的时候 都会自动往本地同步一份
import { useEffect, useState } from "react"
export function useLocalStorage(key,defaultVal){
    const [message,setMessage] = useState(defaultVal)
    useEffect(()=>{
        window.localStorage.setItem(key,message)
    },[message,key])
    return [message,setMessage]

五、useRef——获取真实dom或组件实例

获取真实的dom:获取标签

获取组件实例:类组件(函数组件是没有实例)

1、示例

        获取test组件实例和h1元素

import { Test} from "./Test";
// 导入 useRef 函数
import { useEffect, useRef } from "react";

function App(){  
    // 执行 useRef 函数并传入null,返回值为一个对象
    const title = useRef(null)
    const test =useRef(null)

    // current属性存放dom对象(组件实例)
    useEffect(()=>{
        console.log(title.current,test.current)
    },[])


    return(
            <div>
                {/* 通过ref绑定要获取的对象 */}
                <h1 ref={title}>我是标题</h1>
                <Test ref={test}/>
            </div>
    )
}

export default App;

2、效果

3、总结使用步骤

(1)导入

(2)执行 useRef 函数并传入null const testRef = useRef(null)

(3)通过ref={testRef} 绑定

(4)通过testRef.current 获取

4、useRef 与 createRef

   useRef 仅能用在 FunctionComponent,createRef 仅能用在 ClassComponent

import React,{Component,createRef} from "react";
 
class Test extends Component{
 
  // 使用createRef函数,创建一个存放dom的对象容器
  formRef=createRef()
 
  // 通过this.formRef.current.value获取到表单元素的值
  handleVal=()=>{
   console.log(this.formRef.current.value)
  }
 
  render(){
    return(
      <>
      {/* 通过ref绑定,获取真实dom */}
       <input 
       ref={this.formRef}
       onChange={this.handleVal}>
       </input>
      </>
    );
  }
}

六、useContext ——在hooks下的context使用方式

1、示例

// 导入
import React,{createContext, useContext}from "react";
 
// 创建对象实例
const Context = createContext()
 
function ComA(){
   return(
    <>
        <div>this is SonA</div>
        <ComC/>
    </>
   )
}
 
function ComC(){
     // 通过useContext使用数据
     const name = useContext(Context)
    return(
       <div>ComC接收到App的name是: {name}</div>
    )
 }
 
function App(){
    const name='kk'
        return (
            // 通过Context.Provider标签提供数据
            <Context.Provider value={name}>
              <ComA></ComA>
            </Context.Provider>
            
          );
    }

 
export default App;

2、对比之前的

其中Provider和Consumer都是从解构得到的:const { Provider, Consumer } = createContext()

 // 通过Provider标签提供数据
    <Provider value={name}>
        <ComA></ComA>
    </Provider>
// 通过Consumer获取数
    <Consumer>
        {value=><div>this is SonC,我得到来自App的数据是{value}</div>}
    </Consumer>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值