一、认识hooks
1、为什么需要hooks
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GkF5usIB-1649343862208)(img/为什么需要hooks.png)]
2.class组件存在的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fYihOeL0-1649343862209)(img/class组件存在的问题.png)]
3.hook的出现
注意hook只能在函数式组件中使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MkUbM87w-1649343862211)(img/hook的出现.png)]
4.计数器案例
看之前的代码,发现大量使用this
class组件的写法
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor(props) {
super(props)
this.state = {
counter: 0
}
}
render() {
return (
<div>
<h2>当前计数:{this.state.counter}</h2>
<button onClick={e => { this.setState({counter: this.state.counter + 1})}}>+1</button>
<button onClick={e => { this.setState({counter: this.state.counter - 1})}}>-1</button>
</div>
)
}
}
hooks的写法,只能在函数组件中使用
import React, { useState } from 'react'
export default function App() {
/**
* Hook:useState
* > 本身是一个函数,来自react包
* > 参数和返回值
* 1.参数:作用是给创建出来的状态一个默认值
* 2.返回值:数组
* 元素一:当前state的值
* 元素二:设置新的值时,使用的一个函数
*/
// const arr = useState(0)
// const state = arr[0]
// const setState = arr[1]
// 通常会像下面这样写
// const [count, setCount] = [元素1, 元素2] = useState(默认值)
const [state, setState] = useState(0)
return (
<div>
<h2>当前计数:{state}</h2>
<button onClick={() => { setState(state + 1) }}>+1</button>
<button onClick={() => { setState(state - 1) }}>-1</button>
</div>
)
}
二、认识useState()
1.useState解析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dWU5sDy9-1649343862211)(img/useState解析.png)]
2.认识useState()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GhndxgLQ-1649343862212)(img/认识useState.png)]
3.使用useState()
import React, { useState } from 'react'
export default function MultiHookState() {
// 多个状态多次调用useState函数
const [count, setCount] = useState(0)
const [age, setAge] = useState(18)
// 复杂状态的修改
const [friends, setFriends] = useState(['kebe', 'lilei', 'saSha'])
let newFriends = [...friends]
const addFriends = (value) => {
newFriends = [...friends, value]
}
const [students, setStudents] = useState([
{ name: 'kebe', age: 18, height: 1.88 },
{ name: 'lihua', age: 18, height: 1.88 },
{ name: 'hanmeimei', age: 18, height: 1.88 }
])
function studentAddAge(index) {
const newStudents = [...students]
newStudents[index].age += 1
setStudents(newStudents)
}
return (
<div>
<h2>当前计数: {count}</h2>
<h2>当前年龄: {age}</h2>
<h2>好友列表:</h2>
<ul>
{
friends.map((item, index) => {
return <li key={index}>{item}</li>
})
}
</ul>
<input type="text" placeholder='添加新朋友' onInput={e => addFriends(e.target.value)} />
<button onClick={e => { setFriends(newFriends) }}>添加朋友</button>
<h2>学生列表</h2>
<ul>
{
students.map((item, index) => {
return (
<li key={item.name}>
<span>姓名:{item.name}, 年龄: {item.age}, 身高:{item.height}</span>
<button onClick={e => studentAddAge(index)}>age+1</button>
</li>
)
})
}
</ul>
</div>
)
}
4.setState可以接受一个对象,也可以接受一个cb
如果同时多次使用相同的setState(对象)那么会对这几条语句进行合并,只变化一次,
传递cb的话,里面有prev参数,可以实现多次变化,通过prev来变化
三、认识useEffect
1.认识Effect Hook
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nOeKCX4K-1649343862214)(img/认识EffectHook.png)]
import React, { useEffect, useState } from 'react'
export default function App() {
// 第一个参数,接受一个函数,并返回一个函数
// 参数二:数组,有关性能优化,加上一个空数组的话,数据变化的时候,不会重新渲染组件
const [counter, setCounter] = useState(0)
useEffect(() => {
console.log('订阅一些事件');
return () => {
console.log('取消订阅');
}
}, [])
return (
<div>
<h2>当前计数:{counter}</h2>
<button onClick={() => setCounter(counter + 1)}>+1</button>
</div>
)
}
2.清除副作用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-28siYM4D-1649343862214)(img/清除副作用.png)]
3.使用多个effect-hooks
-
使用Hook的其中一个目的就是解决class中生命周期经常将很多的逻辑放在一起的问题:
- 比如网络请求、事件监听、手动修改DOM,这些往往都会放在componentDidMount中;
-
使用Effect Hook,我们可以将它们分离到不同的useEffect中:
-
import React, { useEffect, useState } from 'react' export default function App() { const [counter, setCounter] = useState(0) // 可以使用多个effct来完成多个逻辑 // 传入空的数组,则不依赖任何值,传入counter,则依赖counter,counter变化的话,就会执行回调函数A useEffect(() => { console.log('修改DOM', counter) }, [counter]) useEffect(() => { console.log('订阅事件') }, []) useEffect(() => { console.log('订阅事件') }, []) return ( <div> <h2>当前计数:{counter}</h2> <button onClick={() => setCounter(counter + 1)}>+1</button> </div> ) }
-
-
Hook 允许我们按照代码的用途分离它们, 而不是像生命周期函数那样:
- React 将按照 effect 声明的顺序依次调用组件中的每一个 effect;
4、性能优化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KTrtdvi-1649343862215)(img/effct性能优化.png)]
四、useContext的使用–可以避免在子组件中使用context的value的嵌套
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7qcCuqA4-1649343862216)(img/useContext的使用.png)]
//类组件里面的context嵌套使用不方便,这样就方便太多了
import React, { useContext } from 'react'
import { UserContext, ThemeContext } from '../App'
export default function App() {
const user = useContext(UserContext)
const theme = useContext(ThemeContext)
// console.log(user);
return (
<div>
<h2>{user.name}, {theme.title}</h2>
</div>
)
}
五、useReducer的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDVLmjre-1649343862217)(img/useReducer的使用.png)]
home.js
import React, { useReducer } from 'react'
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, counter: state.counter + 1 }
case 'decrement':
return { ...state, counter: state.counter - 1 }
default:
return state
}
}
export default function Home() {
// const [count, setCount] = useState(0)
const [state, dispatch] = useReducer(reducer, { counter: 0 })
return (
<div>
<h2>Home当前计数:{state.counter}</h2>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
</div>
)
}
profile.js用了home里面的reducer函数,实现代码的复用,一般是将reducer函数抽取到单独的文件夹,这里只是为了演示
import React, { useReducer } from 'react'
// 用的是home里面的reducer实现了代码的复用
import { reducer } from './home'
export default function Home() {
// const [count, setCount] = useState(0)
const [state, dispatch] = useReducer(reducer, { counter: 0 })
return (
<div>
<h2>Home当前计数:{state.counter}</h2>
<button onClick={() => dispatch({ type: 'increment' })}>+1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-1</button>
</div>
)
}
六、useCallback的使用–性能优化
本身就是一个函数
给子组件传入useCallback包裹的函数,当父组件重新渲染,而子组件中的相关属性没有变化时,就不会重新渲染
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-STpHC11F-1649343862218)(img/useCallback的使用.png)]
import React, { useState, useCallback, memo } from 'react'
// 浅层比较,memo函数看函数组件中的props有没有发生更新而决定要不要重新渲染
// 我们发现当HYbutton2这个组件没有被重新渲染,因为用useCallback对他进行了处理,依赖了count,就会看作increment这个属性没有发生改变,所以不会被重新渲染
// useCallback的应用场景:在将一个组件中的函数传递给子元素回调使用时,适用useCallback进行处理
const HYbutton = memo((props) => {
console.log('HYbutton rerender');
return <button onClick={() => props.increment()}>HYbutton+1</button>
})
export default function App() {
// console.log('rerender');
const [count, setCount] = useState(0)
const [show, setShow] = useState(true)
const increment = () => {
// console.log('increment');
setCount(count + 1)
}
// 这里是一个闭包,如果参数2是一个[]的话,就不会随着increment()变化
const increment2 = useCallback(() => {
console.log('increment2');
setCount(count + 1)
}, [count])
return (
<div>
<h2>demo--{count}</h2>
<HYbutton increment={increment}></HYbutton>
<HYbutton increment={increment2}></HYbutton>
<button onClick={() => setShow(!show)}>show切换</button>
</div>
)
}
七、useMemo的使用—性能优化
返回一个对象或者函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrOUpcUG-1649343862218)(img/useMemo的使用.png)]
// 案例一
import React, { useState, useMemo } from 'react'
function calcSum(count) {
console.log('count + rerender');
let total = 0
for (let i = 1; i <= count; i++) {
total += i
}
return total
}
export default function App() {
console.log('rerender');
const [count, setCount] = useState(3)
const [show, setShow] = useState(true)
// const total = calcSum(count)
// 这样的话,除了有关count的变化会引起重新计算,其他变化不会引起重新计算
const total = useMemo(() => {
return calcSum(count)
}, [count])
return (
<div>
<h2>和: {total}</h2>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setShow(!show)}>切换</button>
</div>
)
}
// 案例二
import React, { useState, memo, useMemo } from 'react'
// memo包裹子组件,当父组件重新渲染的时候,子组件会进行一个浅比较,有变化,重新渲染,没变化,不重新渲染
// 但是每一次父组件重新渲染的时候,info都会重新定义,所以props也算是改变了,所以子组件还是跟着重新渲染
// 要达到子组件不改变的效果
// 1.可以用useState把info包裹起来
// 2.用useMemo包裹起来
const HYinfo = memo((props) => {
console.log('子组件+rerender');
return <h2>名字: {props.info.name} 性别: {props.info.sex}</h2>
})
export default function App() {
console.log('父rerender');
const [show, setShow] = useState(true)
// const [info, setInfo] = useState({ name: 'sasha', sex: '男' })
// const info = { name: 'sasha', sex: '男' }
const info = useMemo(() => {
return { name: 'sasha', sex: '男' }
},[])
return (
<div>
<HYinfo info={info}></HYinfo>
<button onClick={() => setShow(!show)}>切换</button>
</div>
)
}