Hooks 是 React v16.8 中的新增功能
为函数组件提供状态、生命周期等原本 class 组件中提供的 React 功能
可以理解为通过 Hooks 为函数组件钩入 class 组件的特性
注意:Hooks 只能在函数组件中使用,自此,函数组件成为 React 的新宠儿
可以在项目中同时使用hooks和class
一个 Hook 就是一个特殊的函数,让你在函数组件中获取状态等 React 特性
useState使用场景:当你想要在函数组件中,使用组件状态时,就要使用 useState Hook 了
useState作用:为函数组件提供状态(state)
import { useState } from 'react'
const Count = () => {
// stateArray 是一个数组
const stateArray = useState(0)
const state = stateArray[0]
const setState = stateArray[1]
return (
<div>
{/* 展示状态值 */}
<h1>状态为:{state}</h1>
{/* 点击按钮,让状态值 +1 */}
<button onClick={() => setState(state + 1)}>+1</button>
</div>
)
}
import { useState } from 'react'
const Count = () => {
// 解构:
const [count, setCount] = useState(0)
return (
<div>
<h1>计数器:{state}</h1>
<button onClick={() => setState(state + 1)}>+1</button>
</div>
)
}
读取状态:useState 提供的状态,是函数内部的局部变量,可以在函数内的任意位置使用
const Counter = () => {
const [user, setUser] = useState({ name: 'jack', age: 18 })
return (
<div>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
</div>
)
}
修改状态:
setCount(newValue) 是一个函数,参数表示:新的状态值
调用该函数后,将使用新的状态值替换旧值
修改状态后,因为状态发生了改变,所以,该组件会重新渲染
const Counter = () => {
const [user, setUser] = useState({ name: 'jack', age: 18 })
const onAgeAdd = () => {
setUser({
...user,
age: user.age + 1
})
}
return (
<div>
<p>姓名:{user.name}</p>
<p>年龄:{user.age}</p>
<button onClick={onAgeAdd}>年龄+1</button>
</div>
)
}
函数组件使用 useState hook 后的执行过程,以及状态值的变化:
组件第一次渲染:
1.从头开始执行该组件中的代码逻辑
2.调用 useState(0) 将传入的参数作为状态初始值,即:0
3.渲染组件,此时,获取到的状态 count 值为: 0
组件第二次渲染:
1.点击按钮,调用 setCount(count + 1) 修改状态,因为状态发生改变,所以,该组件会重新渲染
2.组件重新渲染时,会再次执行该组件中的代码逻辑
3.再次调用 useState(0),此时 React 内部会拿到最新的状态值而非初始值,比如,该案例中最新的状态值为 1
4.再次渲染组件,此时,获取到的状态 count 值为:1
核心代码
import { useState } from 'react'
const Count = () => {
const [count, setCount] = useState(0)
return (
<div>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
内容:
使用场景:当你想要在函数组件中,处理副作用(side effect)时,就要使用 useEffect Hook 了
作用:处理函数组件中的副作用(side effect)
问题:副作用(side effect)是什么?
回答:在计算机科学中,如果一个函数或其他操作修改了其局部环境之外的状态变量值,那么它就被称为有副作用
类比,对于 999 感冒灵感冒药来说:
(主)作用:用于感冒引起的头痛,发热,鼻塞,流涕,咽痛等
副作用:可见困倦、嗜睡、口渴、虚弱感
理解:副作用是相对于主作用来说的,一个功能(比如,函数)除了主作用,其他的作用就是副作用
对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM)
常见的副作用(side effect):数据(Ajax)请求、手动修改 DOM、localStorage、console.log 操作等
总结:
对于react组件来说,除了渲染UI之外的其他操作,都可以称之为副作用
let a = 1
const Count = () => {
const [count, setCount] = useState(0)
const handleClick = () => {
setCount(count + 1)
}
console.log('aaa')
axios.post('http://xxx')
localStorage.setItem()
a = 2
return (
<div>
<h1>计数器:{count}</h1>
<button onClick={handleClick}>+1</button>
</div>
)
}
使用场景:当你想要在函数组件中,处理副作用(side effect)时就要使用 useEffect Hook 了
作用:处理函数组件中的一些副作用(side effect)
注意:在实际开发中,副作用是不可避免的。因此,react 专门提供了 useEffect Hook 来处理函数组件中的副作用
语法:
核心代码
import { useEffect } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
useEffect(() => {
document.title = `当前已点击 ${count} 次`
})
return (
<div>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
内容:
问题:如果组件中有另外一个状态,另一个状态更新时,刚刚的 effect 回调也会执行
默认情况:只要状态发生更新 useEffect 的 effect 回调就会执行
性能优化:跳过不必要的执行,只在 count 变化时,才执行相应的 effect
语法:
核心代码
import { useEffect } from 'react'
const Counter = () => {
const [count, setCount] = useState(0)
const [loading, setLoading] = useState(false)
useEffect(() => {
document.title = `当前已点击 ${count} 次`
}, [count])
return (
<div>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setLoading(!loading)}>切换 loading</button>
</div>
)
}
内容:
const App = () => {
const [count, setCount] = useState(0)
// 错误演示:
useEffect(() => {
document.title = '点击了' + count + '次'
}, [])
return (
<div>
<h1>计数器:{count}</h1>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
useEffect完全指南:https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
内容:
useEffect 的第二个参数,还可以是一个空数组([]),表示只在组件第一次渲染后执行 effect
使用场景:1 事件绑定 2 发送请求获取数据 等
语法:
useEffect(() => {
const handleResize = () => {}
window.addEventListener('resize', handleResize)
}, [])
注意:
内容:
componetWillUnmount
核心代码:
useEffect(() => {
const handleResize = () => {}
window.addEventListener('resize', handleResize)
// 这个返回的函数,会在该组件卸载时来执行
// 因此,可以去执行一些清理操作,比如,解绑 window 的事件、清理定时器 等
return () => window.removeEventListener('resize', handleResize)
}, [])
// 1
// 触发时机:1 第一次渲染会执行 2 每次组件重新渲染都会再次执行
// componentDidMount + ComponentDidUpdate
useEffect(() => {})
// 2(使用频率最高)
// 触发时机:只在组件第一次渲染时执行
// componentDidMount
useEffect(() => {}, [])
// 3(使用频率最高)
// 触发时机:1 第一次渲染会执行 2 当 count 变化时会再次执行
// componentDidMount + componentDidUpdate(判断 count 有没有改变)
useEffect(() => {}, [count])
// 4
useEffect(() => {
// 返回值函数的执行时机:组件卸载时
// 在返回的函数中,清理工作
return () => {
// 相当于 componentWillUnmount
}
}, [])
useEffect(() => {
// 返回值函数的执行时机:1 组件卸载时 2 count 变化时
// 在返回的函数中,清理工作
return () => {}
}, [count])
内容:
在组件中,可以使用 useEffect Hook 来发送请求(side effect)获取数据
注意:effect 只能是一个同步函数,不能使用 async
为了使用 async/await 语法,可以在 effect 内部创建 async 函数,并调用
核心代码:
// 错误演示:不要给 effect 添加 async
useEffect(async () => {
const res = await axios.get('http://xxx')
return () => {}
}, [])
// 正确使用
useEffect(() => {
const loadData = async () => {
const res = await axios.get('http://xxx')
}
loadData()
return () => {}
}, [])
需求:发起axios请求本地某json数据
第一步:需要将json文件放在 public 目录下 !!!
第二步:引入axios import axios from 'axios'
第三步:发起请求
import React, { useEffect, useState } from 'react'
import axios from 'axios'
export default function App() {
const [list, setList] = useState([])
useEffect(() => {
//接口地址可省略 public
axios.get('http://localhost:3000/data.json').then((res) => {
console.log('打印res', res.data)
setList(res.data.list)
})
}, [])
return (
<ul>
{list.map((item) => (
<li key={item.id}>{item.comment}</li>
))}
</ul>
)
}