问题: 相对于类组件, 函数组件的编码更简单, 效率也更高, 但函数组件不能有state (旧版)
解决: React 16.8版本设计了一套新的语法来让函数组件也可以有state
-
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
-
Hook也叫钩子,本质就是函数,能让你使用 React 组件的状态和生命周期函数...
-
Hook 语法 基本已经代替了类组件的语法
-
后面的 React 项目就完全是用Hook语法了
Hook API 索引 – React 官方文档: Hook API 索引 – React
hook函数和普通函数的区别:
hook函数本身就是一个函数。react通过函数名来判断是普通函数还是hook函 数,以useXxx 格式命名的就是hook函数。
Hook函数使用原则:
1.不能在类组件中使用,不能在普通函数中使用
2.只能在函数组件中使用,或其他hook函数中使用【react提供的,第三方的,自定义的】
3..hook函数必须是数量确定的,不能写在逻辑判断中或后,不能写在循环中
4.应用时,一般写在顶级作用域的首行 使用场景
-----1.可以在函数组件中使用
let [msg, setMsg] = useState('')
function clickHandler(){
// let [count,setCount] = useState() // 普通函数会报错
}
-----2.自定hook中可以使用其他hook
function useClickHandler(){
let [count,setCount] = useState(0)
}
-----3.如果函数名首字母大写,他会 认为是函数组件,也不会报错
function ClickHandler(){
let [count,setCount] = useState(0)
}
1.useState()
作用:给函数组件添加状态
返回值:是一个数组,第一个元素是状态,第二个元素是设置状态的函数
语法:let [状态, 设置状态函数] = useState(初始值)
import React,{useState} from 'react'
export default function App() {
console.log('App render')
let [count,setCount] = useState(0)
return (
<div>
<h3>App</h3>
<p>{count}</p>
<p><button onClick={()=>{
setCount(1000)
}}>count + 1</button></p>
</div>
)
}
2.useEffect
作用:用来模拟函数组件的生命周期 componentDidMount、componentDidUpdate、ComponentWillUnmounted
2.1.用法:
useEffect(回调函数) : 没有第二个参数 模拟 componentDidMount + componentDidUpdate
useEffect(() => {
console.log('useEffect')
})
2.2.用法
useEffect(回调函数,[]) 只模拟 componentDidMount
useEffect(()=>{
console.log('useEffect') // componentDidMount
},[])
2.3.用法
useEffect(回调函数,[某 个自身状态(state) , 某个 外部状态(props), .......])
useEffect(() => {
console.log('useEffect')
}, [count])
2.4.用法
useEffect( return ()=>{ } ) 模拟componentWillUnmount
useEffect(()=>{
console.log('Test useEffect')
return ()=>{ // componentWillUnmount
console.log('destroy')
}
},[msg,money])
3.useRef
作用 :可以用它获取dom元素
1. 创建一个ref let divRef = useRef()
2. 绑定ref
3. 获取dom元素
import React, { useRef } from "react";
export default function App() {
let divRef = useRef();
return (
<div>
<div ref={divRef}>
<h3>app</h3>
<button onClick={()=>{
console.log(divRef.current);
}}>获取DOM元素</button>
</div>
</div>
);
}
4.useContext
作用 :获取从祖先组件传递给后代组件的数据
4.1.创建context对象
context.js 代码
import React from 'react'
// 1. 创建context对象,并暴露出去
const context = React.createContext()
export default context
4.2.使用Provider组件包裹 组件, 并通过 value 绑定要传的数据
App.jsx 代码
import React from "react";
import Father from "./components/Father50";
import context from "./context";
export default function App() {
return (
// 2. 使用Provider包裹组件,并通过value绑定要传输的数据
<context.Provider value={{ name: "App的内容" }}>
<div>
<h3>App</h3>
<hr />
<Father />
</div>
</context.Provider>
);
}
4.3. 引入context对象
4.4.通过useContext处理context对象,获取祖先组件传递的数据
import React from 'react'
// 3. 引入context对象
import context from '../context'
import { useContext } from "react";
export default function Father() {
// 4. 通过useContext处理context对象,获取祖先组件传递的数据
let {name} = useContext(context)
return (
<div>
<h4>Father</h4>
<p>Father-context: {name}</p>
<hr />
</div>
)
}
5.useReducer
集中状态管理。相当于是简化版的 redux
import React, { useState } from 'react'
import { useReducer } from 'react'
const initalState = { count: 0, msg: 'atguigu' }
function reducer(state, action) {
switch (action.type) {
case 'inc':
return {
...state,
count: state.count + 1
}
case 'dec':
return {
...state,
count: state.count - 1
}
case 'add':
return {
...state,
msg:state.msg + '+'
}
default:
throw new Error('没有处理case')
}
}
export default function App() {
let [state, dispatch] = useReducer(reducer, initalState)
return (
<div>
<p>count: {state.count}</p>
<p>msg: {state.msg}</p>
<p><button onClick={()=>{
dispatch({type:'inc'})
}}>count + 1</button></p>
<p><button onClick={()=>{
dispatch({type:'add'})
}}>msg + '+'</button></p>
</div>
)
}
6.useCallBack
可以缓存一个函数。避免函数的多次创建。性能优化
用法一:
没有第二个参数,函数仍然会被重复创建
let clickHandler = useCallback(() => {
setCount(count + 1);
});
用法二:
第二个参数是空数组,那么函数会被缓存
let clickHandler = useCallback(()=>{
// setCount(count + 1)
// 函数被缓存,可以使用setXxx 第二种用法,获取最新的状态值
setCount(count=>count + 1)
},[])
用法三:
第二个参数是数组,并监听 x 个 状态,当这些状态中的一个或多个发生变化时,重新创建函数
let clickHandler = useCallback(() => {
setCount(count + 1);
// 函数被缓存,可以使用setXxx 第二种用法,获取最新的状态值
// setCount(count=>count + 1)
}, [count]);
7.React.memo
作用:类似于类组件中的纯组件。当自身状态和外部数据没有变化的时候,不会重新渲染
App.jsx 代码
import React, { Component } from 'react'
import Test from './components/Test56'
export default class App extends Component {
state = {
msg:'React'
}
render() {
console.log('App render')
return (
<div>
<h3>App</h3>
<p>msg: {this.state.msg}</p>
<p><button onClick={()=>this.setState({
msg:'React'
})}>msg change</button></p>
<hr />
<Test msg={this.state.msg}/>
</div>
)
}
}
Test.jsx 代码
import React from 'react'
import { useState } from 'react'
function Test({msg}) {
console.log('Test render')
// useState已经对自身状态做过优化
let [count,setCount] = useState(0)
return (
<div>
<p>count:{count}</p>
<p>App-msg: {msg}</p>
<button onClick={()=>{
setCount(100)
}}>count + 1</button>
</div>
)
}
export default React.memo(Test)
如图所示:
8.useMemo
作用:缓存一个函数计算的结果,常用来跟useCallback进行比较;useCallback是缓存一个 函数,useMemo缓存函数执行的结果
通俗来讲就是:它是一个优化性能的 Hook,它会记住函数的返回值,只要依赖项(dependency array)没有变化,就会复用之前的计算结果,避免在每次渲染时都重新执行这个可能开销较大的计算
官方详解:
App.jsx 代码
import React from 'react'
import Test from './components/Test57'
export default function App() {
return (
<div>
<Test/>
</div>
)
}
Tset.jsx 代码
import React, { useState,useMemo } from 'react';
export default function Test() {
const [count,setCount] = useState(0)
const [val,setVal] = useState(0)
const expensive = useMemo(()=>{
console.log('================');
let sum =0
for(let i=1;i<count;i++){
sum += i
}
return sum
},[count])
// const expensive = (()=>{
// console.log('================');
// },[count])
return <div>
<h4>{count}-{val}-{expensive}</h4>
<div>
<button onClick={()=>setCount(count + 1)}>+c1</button>
<input val={val} onChange={event =>{
setVal(event.target.value)
}}/>
</div>
</div>;
}
9.useImperativeHandle
它与 forwardRef
结合使用以暴露自定义组件的 refs 给父组件。可以在使用 `ref` 时自定义暴露给父组件的实例值
官方文档:useImperativeHandle – React
APP.jsx 代码
import React from 'react'
import { useRef } from 'react'
import FunTest from './components/FunTest58'
export default function App() {
// ref可以给绑定类组件,并且可以获取类组件实例对象
let refClass = useRef()
// ref本身不能够给函数组件使用,但是可以通过 React.forwardRef()进行扩展
let refFn = useRef('true')
/**
* 当希望在父组件获取子组件的dom对象的时候,可以使用 函数组件配合 React.forwardRef()实现
*/
return (
<div>
<FunTest ref={refFn} />
<p><button onClick={() => {
console.log(refClass)
console.log(refFn)
}}>获取ref</button></p>
<p><button onClick={() => {
refFn.current.changeBg()
}}>changeBg</button></p>
<p><button onClick={() => {
refFn.current.changeFontSize()
}}>changeBg</button></p>
</div>
)
}
FunTest.jsx 代码
import React from "react";
import { useRef, useImperativeHandle } from "react";
function FunTest(props, AppRef) {
let selRef = useRef()
useImperativeHandle(AppRef,()=>({
changeBg:()=>{
selRef.current.style.backgroundColor = "red"
}
}))
return (
<div>
<h3 ref={selRef}>FunTest</h3>
</div>
);
}
export default React.forwardRef(FunTest);
如图所示:
10.useLayoutEffect
useLayoutEffect
是useEffect的一个版本,在浏览器重新绘制屏幕之前触发。
useEffect在render结束后,你的callback函数执行,但是不会阻塞浏览器渲染
作用:用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否 则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在 浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制
官方文档:useLayoutEffect – React
App.jsx 代码
import React from 'react'
import Animate from './components/Animate59'
export default function App() {
return (
<div>
<Animate/>
</div>
)
}
Animate.jsx 代码
import React, { useEffect, useLayoutEffect, useRef } from 'react'
import TweenMax from 'gsap' // npm i gsap@3.7.0
import '../index.css'
const Animate = () => {
const REl = useRef(null)
useLayoutEffect(()=>{
TweenMax.to(REl.current,0,{x:600})
},[])
return (
<div className="animate">
<div ref={REl} className="square">
square
</div>
</div>
)
}
export default Animate
11.useDebugValue
作用:用于在 React 开发者工具中显示 自定义 hook 的标签,只能在自定义hook中使用
官方文档:useDebugValue – React
12.useId
用于生成一个唯一的标识
import React from 'react'
import { useId } from 'react'
export default function App() {
let id1 = useId()
let id2 = useId()
console.log(id1);
console.log(id2);
return (
<div>
<div>App</div>
</div>
)
}
13.useTransition
作用:可以将任务设置为非紧急任务
官方文档:useTransition – React
const [isPending, startTransition] = useTransition()
startTransition(()=>{
})
14.useDeferredValue
作用:根据一个状态,设置一个延时的状态。也可以实现,任务渲染的优先级区别
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}
15.自定义hook函数
作用: 函数组件代码逻辑复用的手段,函数名 useXxx 格式 ,函数中可以使用其他hook函数
App.jsx 代码
import React, {useState} from 'react'
import { useEffect } from 'react'
import usePosition from '../hook/usePosition'
export default function Cat() {
let {x,y} = usePosition()
return (
<div style={{width:100,height:100,border:'1px solid red',position:'absolute',left:x,top:y}}>Cat</div>
)
}
usePosition.js 代码
import {useState, useEffect} from 'react'
export default function usePosition() {
let [x, setX] = useState(0)
let [y, setY] = useState(0)
function moveHandler(e) {
setX(e.clientX)
setY(e.clientY)
}
useEffect(() => {
window.addEventListener('mousemove', moveHandler)
return () => {
window.removeEventListener('mousemove', moveHandler)
}
}, [])
return {x,y}
}