一、简单介绍一下Hooks
1.Hooks出现的意义
react组件的创建方式有两种: 类组件和函数组件.在以往开发类组件的时候, 每一个类都有自己的状态, 但是在函数组件中是没有状态的概念的.
react组件的状态是什么? 组件的状态(state)是一个对象,它包含某些信息,这些信息可能在组件的生命周期中发生更改。开发时只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
那么在Hooks出现之后呢, 才让函数组件出现了状态这个概念, 每一个函数组件的状态独立管理.
并且在以往类组件开发组件的时候, 类组件想要复用, 要遵循类组件的规则, Hooks出现后, 可以让我们更加灵活的创建自己的组件.
总结下来React Hooks出现的意义:
- Hooks之间的状态是独立的, 有自己的独立上下文, 不会出现混淆情况.
- 让函数组件有了状态管理.
- 解决了组件树不直观, 类组件难维护.逻辑不复用的问题
- 由于函数每次渲染都会执行, 所以React中多了一种状态控制, 传入第二个参数. 这样就可以避免函数重复执行
2. 为什么要学习Hooks?
- 高效开发
- 提高组件的复用性.(函数组件)
- 提高逻辑的复用性
- 提升性能
- 实现更复杂的功能
3. Hooks的具体使用场景
- 利用Hooks取代生命周期
- 让函数组件加上状态
- 组件的辅助函数
- 处理发送请求
- 存储数据
- 做好性能优化
二、React hooks有哪些API
1. useState() : 定义函数组件的状态
作用:
- 初始化以及更新state
- 用来声明状态变量
import React, { useState } from “react”
function StateFunction() {
// 使用useState来创建状态
// 接收一个参数作为初始值
// 返回值中第一个参数代表状态变量, 第二个代表修改这个变量的函数
const [buttonText, setButtonText] = useState(“click me, please”)
function handleClick() {
return setButtonText(“Thanks, been clicked!”)
}
return <button onClick={handleClick}>{buttonText}</button>
}
export default StateFunction
2. useEffect(): 副作用钩子
作用:
- 给没有生命周期的组件, 添加结束渲染的信号.
- 组件中可以多次使用.
useEffect()接受两个参数,第一个参数是要进行的异步操作,第二个参数是一个数组,用来给出Effect的依赖项,只要这个数组发生变化,useEffect()就会执行。
当第二项省略不填时。useEffect()会在每次组件渲染时都会执行useEffect,只要更新就会执行。
当第二项传 空数组[ ] 时,只会在组件挂载后运行一次。
useEffect()返回值可以是一个函数,在组件销毁的时候会被调用。清理这些副作用可以进行如取消订阅、清除定时器操作,类似于componentWillUnmount。
const Person = ({ personId }) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
// 接收函数作为一个参数
// 接收一个函数作为参数
// 接收第二个参数, 依赖列表只有依赖更新时, 才会执行函数
// 返回一个函数, 先执行返回函数, 在执行参数函数
useEffect(() => {
setLoading(true)
fetch(‘www.api.xxx’)
.then(response => response.json())
.then(data => {
setPerson(data)
setLoading(false)
})
console.log(“函数组件渲染”)
return () => {
console.log(销毁)
}
}, [person])
if (loading === true) {
return <p>Loading…</p>
}
return
<div>
<p>name: {person.name}</p>
</div>
}
可以把 useEffect Hook 看做如下三个函数的组合 :componentDidMount()、componentDidUpdate()、componentWillUnmount()
useEffect Hook是如何模拟声明周期的:
模拟 componentDidMount 和 componentDidUpdate,useEffect 依赖不写
模拟 componentDidMount,useEffect 依赖 []
模拟 componentDidUpdate,useEffect 无依赖,或者依赖 [a, b]
模拟 componentWillUnMount,useEffect 返回一个函数
3. useLayoutEffect: 副作用钩子, 在dom更新完成之后执行某个操作,
与useEffect不同之处:
- useEffect执行时机是在render之后, useLayoutEffect是在dom更新之后
- useEffect是同步的, 而useLayoutEffect是同步的
import React, { useState, useLayoutEffect } from “react”
function StateFunction () {
const [num, setNum] = useState(1)
// 接收函数作为一个参数
// 接收一个函数作为参数
// 接收第二个参数, 依赖列表只有依赖更新时, 才会执行函数
// 返回一个函数, 先执行返回函数, 在执行参数函数
// 不同点
// useEffect执行时机是在render之后, useLayoutEffect是在dom更新之后
// useEffect是同步的, 而useLayoutEffect是同步的
useLayoutEffect(() =>{
console.log(‘useLayoutEffect’)
document.body.addEventListener(‘a’, ()=> {})
return() => {
document.body.removeEventListener(‘a’, ()=> {})
}
, [num])
useEffect(() => {
console.log(‘useEffect’)
}, [num])
return <div onClick=“{()=>{ setNum(num => num + 1) }}”></div>
}
export default StateFunction
4. useMemo: 让组件中的函数跟随状态跟新, 返回的是一个值
作用: 主要用来解决使用React hooks产生的无用渲染的性能问题。
// 1. 接受一个函数作为参数
// 2. 第二个参数为依赖列表, 对比useEffect、useLayoutEffect
// 3. 返回的是一个值
import React, { useState, useMemo } from “react”
function StateFunction () {
const [num, setNum] = useState(1)
const [age, setAge] = useState(18)
const getDoubleAge = useMemo(()=>{
console.log(“获取2倍的age”)
return 2 * num
}, [age])
return <div onClick=“{()=>{ setNum(num => num + 1) }}”>
{ getDoubleAge }
</div>
}
export default StateFunction
useMemo应用场景:
- 可以缓存 element 对象,从而达到按条件渲染组件,优化性能的作用。
- 如果组件中不期望每次 render 都重新计算一些值,可以利用 useMemo 把它缓存起来。
- 可以把函数和属性缓存起来,作为 PureComponent 的绑定方法,或者配合其他Hooks一起使用
5. useCallback: 组件中的函数跟随状态更新, 返回的是一个函数
作用:
- 只有依赖项改变的时候才会更新, 为了更好的性能的优化
- useMemo相当于(() => {}, deps[]), 相当于 useCallback(fn, deps[])
// 1. 只有依赖项改变时, 才会执行
// 2. 在使用方法上, useMemo(()=>fn, deps) 相当于 useCallback(fn, deps)
import React, { useState, useCallback } from “react”
function StateFunction () {
const [num, setNum] = useState(1)
const [age, setAge] = useState(18)
const getDoubleAge = useCallback(()=>{
console.log(“获取2倍的age”)
return 2 * num
}, [age])
set.add(getDoubleNum)
console.log(set.size)
// 3. 不同点: useCallback返回的是一个函数, useMemo返回的是一个值
return <div onClick=“{()=>{ setNum(num => num + 1) }}”>
{ getDoubleAge() }
</div>
<Child callback={getDoubleNum}></Child>
}
function Child(props) {
useEffect(() => {
console.log(‘callback更新了’)
}, [props.callback])
return <div>child</div>
}
export default StateFunction
6. userRef: 长久保存数据
作用:
- 返回一个子元素的索引, 此索引在整个生命周期中保持不变.
- 对象发生改变, 不通知. 属性变更不重新渲染.
(1) 在整个生命周期中保持一个数据不变
import React, { useState, useEffect, useRef } from “react”
function StateFunction () {
const [num, setNum] = useState(1)
const ref = useRef()
console.log(ref)
useEffect(() => {
ref.current = setInterval(() => {
setNum(num => num + 1)
}, 400)
}, [])
useEffect(() => {
if (num > 10) {
console.log(“超过10了”, ref.current)
}
}, [])
return <div >{ num }</div>
}
export default StateFunction
(2) 对象放生改变, 不通知. 属性变更不重新渲染
import React, { useState, useEffect, useRef } from “react”
function StateFunction () {
const [num, setNum] = useState(1)
const ref = useRef()
console.log(ref)
useEffect(() => {
ref.current = ‘1111’
}, [])
return <div >{ ref.current }</div>
}
7. useContext: 共享状态钩子
作用: 用于组件之间的共享状态
第一步: 引入useContext、 createContext两个内容
第二步: 通过createContext创建context句柄
第三步: Context.Provider确定数据共享范围
第四步: 通过value分发内容
第五步: 子组件, 通过useContext 传入数据
例: 现在有两个组件Navbar 和 Messages, 我们希望他们之间共享钩子
import React, { useContext } from “react”
const AppContext = React.createContext({});
const Navbar = () => {
const{ userName } = useContext(AppContext)
return (
<div className=“navbar”>
<p>{ userName }</>
</div>
)
}
const Messages = () => {
const { userName } = useContext( AppContext )
return (
<div className=“message”>
<p>{ userName }</p>
</div>
)
}
function App() {
return (
//
<AppContext.Provider value={{
userName: ‘super man’
}}>
<div>
<Navbar />
<Messages />
</div>
</AppContext.Provider>
)
}
8. useReducer: Action钩子
作用: React本身不提供状态表管理功能, 通常需要使用外部库, 最常用的是redux, Redux最核心的概念是, 组件发出action 与状态管理器通信, 状态管理器收到Action以后, 使用render函数算出新的状态.
useReducer的使用方法:
1. 需要创建数据仓库 store 和管理者 reducer
2. 通过useReducer ( reducer, store ) 来获取state 和 dispatch
// 使用useRender()钩子用来引入reducer功能
// redux的必备内容
// store reducer
Import React, { useRenducer } from “react”
const myReducer = (state, action) => {
switch(action.type) {
case(“countUp”):
return {
…state,
count: state.count -1
}
default:
return state
}
}
function App() {
const [state, dispatch] = useReducer(myReducer, { count: 0 })
return (
<div>
<button onClick={() => dispatch({ type: ‘countUp’ })}></button>
<p>Count: { state.count }</p>
</div>
)
}
const rootElement = document.getElementById(“root”)
ReactDOM.render(<APP />, rootElement)
使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。
9. 自定义Hooks: 自定义Hooks以支持特殊场景
- 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook
- 可以封装状态,能够更好的实现状态共享
import { useState,useEffect } from "react";
const usePerson = ({name}) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
useEffect(() => {
setLoading(true)
setTimeout(()=> {
setLoading(false)
setPerson({name})
},2000)
},[name])
return [loading,person]
}
const AsyncPage = (name)=> {
const [loading,person] = usePerson(name)
return (
<>
{loading?<p>Loading...</p>:<p>{ person.name }</p>}
</>
)
}
const PersonPage = ()=> {
const [state,setState] = useState('')
const changeName = (name)=> {
setState(name)
}
return (
<>
<AsyncPage name={ state } />
<button onClick={ ()=> { changeName('郭靖')}}>郭靖</button>
<button onClick={ ()=> { changeName('黄蓉')}}>黄蓉</button>
</>
)
}
export default PersonPage;