如果你和我一样都是React新手,相信这篇文章的内容可以帮助你学习React Hooks,详细介绍了每个Hooks的作用和使用方法,以及使用案例。欢迎收藏、点赞、转发哦~
1 React Hooks的作用:
- 对函数型组件进行增强,让函数型组件可以存储状态,可以拥有处理副作用的能力。
- 让开发者在不使用类组件的情况下,实现相同的功能。
副作用: 组件中只要不是把数据转换成视图的代码都属于副作用,比如获取DOM元素,为DOM元素添加事件,ajax请求,设置定时器等,在类组件中我们通常使用生命周期函数来处理副作用,但在函数组件中使用hooks来处理副作用。
2 类组件的不足
- 缺少逻辑复用机制
为了复用逻辑增加无实际渲染效果的组件,增加了组件层级,显示十分臃肿;增加调试的难度;运行效率降低;
- 类组件经常会变得很复杂难以维护
将一组想干的业务逻辑拆分到了多个生命周期函数中,在一个生命周期函数内存在多个不相干的业务逻辑。
- 类成员方法不能保证this指向的正确性
需要使用bind和函数嵌套函数的方式来保证this指向,复杂而难以维护
3 React Hooks使用
React Hooks就是一堆钩子函数,”钩入“React特性的函数。React通过这些钩子函数对函数型组件进行增强,不同的钩子函数提供了不同的功能。
3.1 useState
作用:用于为函数组件引入状态,使函数型组件可以保存状态。原本一个函数里面的变量,在函数执行后就会被释放,useState方法内部是用闭包来实现保存状态数据的。
使用细节:
- 接收唯一的参数既状态初始值,初始值可以是任意数据类型
- 返回值为数组,数组中存储状态值和变更状态值的方法。方法名称约定以set开头,后面加上状态名称。
- 方法可以被调用多次,用以保存不同状态值。
- 参数可以是一个函数,函数返回什么,初始状态就是什么,函数只会被调用一次,用在初始状态值是动态的值的情况。
import React, { useState } frrom 'react'; function App(props) { const [ count, setCount ] = useState(() => { return props.count || 0 // 函数只会被调用一次 }) const [ person, setPerson ] = useState({name: '张三', age: 20}) return ( <div> <span>{count} {person.name} {person.age}</span> <button onclick={() => setCount(count + 1)}>+1</button> <button onclick={() => setPerson({...person, name: '李四'})}>setPerson</button> </div> ) } export default App
设置状态值方法的参数可以是一个值也可以是一个函数
设置状态值方法的方法本身是异步的
3.2 useReducer()
作用:另一种让函数组件保存状态的方式
使用细节:和redux的使用类似
import React, { useReducer } from 'react'
function App() {
function reducer (state, action) {
switch (action.type) {
case 'increment':
return state + 1;
case 'decreament':
return state - 1;
default:
return state;
}
}
}
const [ count, dispatch ] = useeReducer(reducer, 0)
return (
<div>
<button onclick={() => dispatch({type: 'decrement'})}>-1</button>
<span>{count}</span>
<button onclick={() => dispatch({type: 'increment'})}>-1</button>
</div>
)
export default App;
3.3 useContext()
作用:在跨组件层级获取数据时简化获取数据的代码
import React, { createContext, useContext } from 'react'
const countContext = createContext();
function App() {
return <countContext.Provider value={100}>
<Foo/>
</countContext.Provider>
}
function Foo() {
const value = useContext(countContext);
return <div>我是Foo组件{value}</div>
}
export default App
3.4 useEffect()
作用:让函数组件拥有处理副作用的能力,类似生命周期函数
- useEffect执行时机
可以把useEffect看做
componentDidMount,componentDidUpdate, componentWillUnmount
这三个函数的组合
useEffect(() => {}) --->componentDidMount, componentDidUpdate
useEffect(() => {}, []) --->componentDidMount
useEffect(() => () => {}) --->componentWillUnMount
- useEffect 使用方法
import React, { useEffect, useState } from "react"
import ReactDom from 'react-dom'
function App() {
function onScroll () {
console.log('页面发生滚动了')
}
// 组件挂载之后监听页面滚动,组件卸载之后,滚动时间被解绑
useEffect(() => {
window.addEventListener('scrroll', onScroll)
return () => {
window.removeEventListener('scroll', onScroll)
}
}, [])
return <div>
<span>{count}</span>
<button onclick={() => ReactDom.unmountComponentAtNode(document.getElementById('tooy')}>卸载组件</button>
</div>
}
export default App
-
useEffect 解决的问题:
(1)按照用途将代码进行分类(将一组相关的业务逻辑归置到了同一个副作用函数中)
(2)简化重复代码,使组件内部代码更加清晰。 -
使用useEffect的第二个参数来实现,只有指定数据发生变化时触发effect。
-
useEffect 结合异步函数
useEffect中的参数函数不能是异步函数,因为useEffect函数要返回清理资源的函数,如果是异步函数就变成了返回Promise,可以通过添加自执行函数的方式来结合异步函数。
useEffect(() => {
(async () => {
await axios.get()
})()
})
3.5 useMemo()
作用: 类似于Vue中的计算属性,可以监测某个值的变化,根据变化值计算新值。
useMemo会缓存计算结果,如果监测值没有发生变化,即使组件重新渲染,也不会重新计算,此行为可以有助于避免在每个渲染上进行昂贵的计算。
import React, { useState, useMemo } from 'react'
function App() {
const [count, setCount] = useState(0)
const result = useMemo(() => {
// 如果count值发生变化此函数重新执行
return count * 2
}, [count])
return (
<div>
<span>{count} {result}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
export deefault App
3.6 memo方法
作用:性能优化,如果本组件的数据没有发生变化,阻止组件更新,类似类组件中的PureComponent和ShouldComponentUpdate
import React, { useState, memo } from "react"
function App() {
const [count, setCount] = useState(0)
return (
<div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
<Foo />
</div>
)
}
const Foo = memo(function Foo() {
// memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
console.log("Foo组件重新渲染了")
return <div>我是Foo组件</div>
})
export default App
3.7 useCallback()
作用:性能优化,缓存函数,使用组件重新渲染时得到相同的函数实例。从而避免嵌套函数的再次渲染。
import React, { useState, memo, useCallback } from "react"
function App() {
const [count, setCount] = useState(0)
const resetCount = useCallback(() => setCount(0), [setCount])
return (
<div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+1</button>
<Foo resetCount={resetCount} />
</div>
)
}
const Foo = memo(function Foo(props) {
// memo包裹后,count发生变化,App组件重新渲染,但是子组件中的数据并未发生变化所以Foo不会被重新渲染
console.log("Foo组件重新渲染了")
return (
<div>
我是Foo组件
<button onClick={props.resetCount}>resetCount</button>
</div>
)
})
export default App
3.8 useRef()
作用1:获取DOM元素对象
import React, { useRef } from 'react'
function App () {
const username = useRef()
const handler = () => console.log(username) // { current: input}
return <input ref={username} onChange={handler} />
}
exporrt default App
作用2:保存数据(跨组件周期)
即使组件重新渲染,保存的数据仍然还在,保存的数据被更改不会触发组件重新渲染
import React, { useState, useEffect, useRef } from 'react'
function App() {
const [count, setCount] = useState(0)
let timerId = useRef() // 如此可以避免count 变化时,App组件重新渲染,timerId丢失而无法清除定时器的问题
useEffect() => {
timerId.current = setInterval(() => {
setCount(count => count + 1)
}, 1000)
}, [])
const stopCount = () => {
console.log(timerId)
clearInterval(timerId.current)
}
return (
<div>
{count}
<button onClick={stopCount}>停止</button>
</div>
)
}
export default App
更多关于useRef的介绍请查看useRef详细总结
3.9 路由钩子函数
import React from 'react'
import { useHistory, useLocation, useRouteMatch, useParams } from 'react-router-dom'
export default function Home(props) {
console.log(props)
console.log(useHistory())
console.log(useLocation())
console.log(useRouteMatch())
console.log(useParams())
return <div>Home Workd</div>
}
4 自定义钩子函数
自定义Hook是标准的封装和共享逻辑的方式
自定义Hook是一个函数,起名称以use开头
自定义Hook其实就是逻辑和内置Hook的组合
import React, { useState, useEffect } from 'react'
import axios from 'axios'
function useGetPost () {
const [post, setPost] = useState({})
useEffect(() => {
axios.get('https://test.com/post/1')
.then(responsee => setPost(response.data))
}, [])
return [post, setPost]
}
function App() {
const [post, setPost] = useGetPost()
return (
<div>
<div>{post.title}</div>
<div>{post.body}</div>
</div>
)
}
export default App