Hooks
-
hooks:react为函数式组件提供的一些方法,包含动态数据,生命周期等等 -
hooks:只能写在组件或自定义hooks中 -
按需引入:
import { useState,useEffect,useContext,useReducer,useRef,useLayoutEffect,useMemo,useCallback } from "react"
基础HOOK
⭐useState
-
作用:为函数组件提供动态数据,更新视图
-
语法1:
let [默认数据变量,修改数据的方法] = useState('默认数据') 修改数据方法(新数据) 🌰: let [name,setName] = useState('慢羊羊') setName('懒羊羊')-
基本数据类型
function App() { let [age, setAge] = useState(12) const changeAge = () => { setAge(age + 1) } return ( <div> <h3>喜羊羊{age}岁啦</h3> <button onClick={() => changeAge()}>新年好</button> </div> ); }
-
-
语法2:
let [默认数据变量,修改数据的方法] = useState('默认数据') 数组: 修改数据的方法((state)=>{return[...新数据]}) `形参state为默认数据` 对象: 修改数据的方法((state)=>{自行合并新旧数据}) `形参state为默认数据`-
复杂数据类型--数组
//🌰:添加 function App() { let [list, setList] = useState(['🍉', '🍑']) const changeList = () => { let item = '🍌' setList((state) => { state.push(item) return [...state] }) } return ( <div> { list.map((item, index) => { return <div key={index}>{item}</div> }) } <button onClick={() => changeList()}>添加</button> </div> ); } // 🌰:删除 function App() { let [list, setList] = useState(['🍊', '🍉', '🍑', '🍌', '🍎']) const delList = (index) => { setList((state) => { state.splice(index, 1) console.log(state); return [...state] // 改变引用地址 }) } return ( <div> { list.map((item, index) => { return <div key={index}>{item}<button onClick={() => delList(index)}>删除</button></div> }) } </div> ); } -
复杂数据类型--对象
🌰: function App() { let [obj, setObj] = useState({ name: '小灰灰', age: 6 }) const changeAge = () => { setObj((state) => { return { ...state, age: state.age + 1 } }) } return ( <div> <h5>{obj.name}今年{obj.age}啦</h5> <button onClick={() => changeAge()}>过年了</button> </div> ); }
-
react更新机制
-
react更新机制:重新创建组件
-
问题:
在项目中,有时需要局部更新,其他地方不更新 可以使用useEffect来解决,详情见下文
⭐useEffect
-
useEffect:本质是一个
宏任务 -
副作用:
- 当做组件的生命周期(相当于**
mounted**) - 组件的销毁
- 监听
- 当做组件的生命周期(相当于**
-
基本语法:想要组件加载和更新(工作中不用)
function App() { let [num, setNum] = useState(0) useEffect(() => { console.log('组件加载完毕,组件更新'); }) const changeNum = () => { setNum(num + 1) } return ( <div> <h5>{num}</h5> <button onClick={() => changeNum()}>过年了</button> </div> ); } -
当做组件的生命周期使用
-
语法:
useEffect(处理函数,依赖项)function App() { let [num, setNum] = useState(0) useEffect(() => { console.log('组件加载完毕,组件更新'); }, []) const changeNum = () => { setNum(num + 1) } return ( <div> <h5>{num}</h5> <button onClick={() => changeNum()}>过年了</button> </div> ); } -
如果依赖项为空数组,那么处理函数只会执行一次,相当于生命周期
mounted -
如果没有依赖项,只要组件函数重新执行,处理函数就会重新执行
-
使用场景:DOM操作、发送请求...
-
-
组件的销毁
-
语法:
useEffect的返回值返回一个方法 -
使用场景:清除定时器...
function App() { let [num, setNum] = useState(0) const addNum = () => { setNum(num+1) } return ( <div> {num % 3 == 0 ? <Son></Son> : ''} <button onClick={() => addNum()}>点击</button> </div> ) } // 子组件 function Son() { // 定时器 let time = setInterval(() => { console.log('定时器'); }, 1000) useEffect(() => { return () => { console.log('子组件销毁了') clearInterval(time) } }) return ( <div> <h3>Son</h3> </div> ) }
-
-
监听
-
语法:
useEffect(()=>{},[监听的数据1,监听的数据2]...) -
相当于
vue中的watch -
默认立即执行
function App() { let [age, setAge] = useState(12) let [name, setName] = useState('喜羊羊') useEffect(() => { console.log('监听', age); }, [age]) return ( <div> <h3>{name}--{age}</h3> <button onClick={() => setName('美羊羊')}>姓名</button> <button onClick={() => setAge(age + 1)}>年龄</button> </div> ) }
-
⭐useContext
-
提供上下文,本质是一个全局变量
-
在最外层提供数据,在里面可以获取到数据
-
使用场景:全局设置主体、字体...
-
使用
1.引入react、useContext 2.通过React.createContext创建一个数据,返回一个对象(例:Context),使用对象的Provider属性来提供数据
3.定义要传递的数据
4.在父级组件中使用<Context.Provider />组件结合value属性来提供数据
<Context.Provider value={数据}></Context.Provider>
5.子组件中通过useContext(Context)获取到父级组件提供的数据
```react
// 1.引入
import React, { useContext } from 'react'
// 2.创建一个数据
let Context = React.createContext() //返回一个对象
console.log(Context);
// 3.定义要传递的数据
let theme = {
bgcol: 'red'
}
// 父组件
function App() {
return (
// 4.父级组件使用
<Context.Provider value={theme}>
<div>
<Son></Son>
</div>
</Context.Provider>
)
}
// 子组件
function Son() {
// 5.获取父组件提供的数据
let data = useContext(Context)
console.log(data);
return (
<div style={{ background: data.bgcol }}>子组件</div>
)
}
额外HOOK
⭐useReducer
-
用法和
useState一样,都事用来处理动态数据的 -
作用:对动态数据的行为进行约束
-
语法:
let [默认数据,触发reducer方法] = useReducer(定义的reducer,默认数据) -
reducer是什么?
1.reducer是一个函数 2.reducer函数有2个参数(形参) 参数1:默认数据 参数2:触发reducer方法传入的实参(触发reducer方法`dispatch`传递的参数,一个对象) 3.reducer返回值为最新的数据 4.只能处理同步问题(相当于vuex中的mutations)
// 引入
import { useReducer } from "react";
// 定义reducer
function reducer(state, actions) {
// 逻辑代码
switch (actions.type) {
case '++':
return state + actions.money;
case '--':
return state - 200;
default:
return state;
}
}
function App() {
// 使用useReducer
let [moneys, dispatch] = useReducer(reducer, 1000)
return (
<div>
<h3>余额:¥{moneys}.00</h3>
{/* 通过dispatch触发,传递参数,使用对象 */}
<button onClick={() => {dispatch( {type:'++',money: 500})} }>收红包</button>
<button onClick={() => {dispatch( {type:'--',money:200})} }>发红包</button>
</div>
);
}
⭐useRef
-
作用:获取到元素的真实dom
-
语法:
let dom1 = useRef(null) <元素 ref={dom1} /> -
该组件创建的流程
1.组件创建完毕,初始化组件的属性 2.将组件(模板语法jsx) => vnode 有事件集中处理,有属性进行绑定 3.将vnode => 真实dom 4.做dom操作 总结:在mouted生命周期中可以拿到真实dom
// 引入
import { useRef, useEffect } from "react";
function App() {
// created生命周期
let dom1 = useRef(null) //null
let dom2 = useRef(null) //null
let num = 100
// mouted生命周期
useEffect(() => {
console.log(dom1); //获取真实dom1
console.log(document.getElementById('box'));
console.log(dom2); //获取真实dom2
console.log(document.getElementById('box2'));
}, [])
return (
<div>
<h2 id="box" data-id={num} ref={dom1}>DOM-1</h2>
<h2 id="box2" data-id={num + 10} ref={dom2}>DOM-2</h2>
</div>
)
}
useLayoutEffect
-
语法:与
useEffect一致 -
原理:底层代码是
微任务 -
作用:
当做生命周期使用 1.加载之前 2.更新之前 3.销毁之前 监听 当被监听的数据发生改变之前执行这个处理方法 -
useEffect和useLayoutEffect的区别#***** 他们俩看起来非常相似,事实上他们只有一点区别: `useEffect`:会将渲染的内容更新到dom上,把该显示的都显示了之后,再执行这个回调函数,不会阻塞dom的更新 `useLayoutEffect`:会在dom渲染的内容更新到dom上之前执行。我已经返回了我要显示的东西,你应该显示到dom上了,但是你还没显示到dom上的时候,你先把这个回调函数执行完,再去吧数据更新到dom上。会阻塞dom的更新
// 引入
import { useLayoutEffect, useEffect, useState } from "react";
function App() {
let [num, setNum] = useState(10)
useEffect(() => {
console.log('加载完毕,mounted;', num);
}, [num])
useLayoutEffect(() => {
console.log('挂载前,解析dom前,beforeMount;');
}, [num])
return (
<div>
{num}
<button onClick={() => setNum(num + 1)}>++</button>
</div>
)
}
组件的渲染
1.组件的渲染:先渲染父组件再渲染子组件
2.性能问题:
如果在父组件中更新数据,组件会重新渲染,父组件的嵌套组件(子组件)也会被重新渲染
解决方法:
1、模块化开发,一个功能一个模块(推荐)
2.使用useMemo、useCallback进行缓存(工作中不用,`面试要讲`)
useMemo
-
作用:缓存变量
-
使用场景:此方法比创建变量更消耗性能,项目中不用,面试要说
-
语法:
let 缓存变量 = useMemo(()=>{},[监听数据,...])
// 引入
import { useMemo, useState } from "react";
function App() {
let [age, setAge] = useState(12)
let KeepName = useMemo(() => {
console.log('更新');
return '电击小子' // 缓存数据
}, [])
return (
<div>
<h4>{KeepName} 今年 {age} 岁啦</h4>
<button onClick={() => setAge(age + 1)}>过年啦</button>
</div>
)
}
useCallback
-
作用:将方法缓存
-
使用场景:此方法比创建变量更消耗性能,项目中不用,面试要说
-
语法:
let 方法名 = useCallback(()=>{ },[监听数据])
// 引入
import { useCallback, useState } from "react";
function App() {
let [num, setNum] = useState(12)
// 方法
let fn = () => {
console.log('执行');
}
// 将方法缓存
let KeepFn = useCallback(() => {
return fn()
}, [num])
KeepFn() // 启用这个缓存方法
return (
<div>
<h3>{num}</h3>
<button onClick={() => setNum(num + 1)}>修改</button>
</div>
)
}
useImperativeHandle
useDebugValue
自定义hooks
-
自定义hooks
1.自定义hooks是一个方法(函数) 2.该函数名以`use`开头 3.在该函数中可以使用`react`提供的`api` -
何时写自定义hooks?
react项目中的方法都可以写成自定义hooks