关注公众号,更多精彩内容等着你
前端
React Hook 指南
什么是 Hook ?
❝Hook 是 React 16.8 的新增特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。react
Hook
本质上就是一个函数,它简洁了组件,有本身的状态管理,生命周期管理,状态共享。web❞
useState
useEffect
useContext
useReducer
Hook 出现解决了什么 ?
❝❞
[ ] 组件之间状态复用, 例如:使用useContext 能够很好的解决状态复用问题,或者自定义 Hook
来定制符合本身业务场景遇到的状态管理。[ ] 在函数组件中 生命周期的使用,更好的设计封装组件。在函数组件中是不能直接使用生命周期的,经过 Hook
很好的解决了此问题。[ ] 函数组件与 class 组件的差别,还要区分两种组件的使用场景。使用 Hook
彻底不用去想这些,它可使用更多React
新特性。
何时使用 Hook ?
❝❞
在函数组件顶层调用
在 函数中使用 / 自定义
Hook
中使用
React
内置的 Hook
❝❞
useState
状态管理
useEffect
生命周期管理
useContext
共享状态数据
useMemo
缓存值
useRef
获取Dom 操做
useCallback
缓存函数
useReducer
redux 类似
useImperativeHandle
子组件暴露值/方法
useLayoutEffect
完成反作用操做,会阻塞浏览器绘制
useState
状态管理
❝在
class
组件中,咱们获取state
是 经过this.state
来获取的。面试而在
❞函数组件
中, 是没有this
的, 咱们可使用Hook
提供的useState
来管理和维护state
.redux
useState
定义 / 使用
❝
const [state, setState] = useState(initialState)
数组❞
setState 为更新 satate 方法
useState(initialState) initialState 为初始值
完整栗子
import {useState} from 'react';
export default () => {
const [data, setData] = useState('微信公众号: 前端自学社区')
return (
<div>
<h1>{data}</h1>
{/* 更新 state */}
<button onClick={()=>{setData('微信公众号: 前端自学社区 666')}}></button>
</div>
)
}
useEffect
生命周期管理
定义
❝
useEffect
能够看做是函数式 组件
的 生命周期管理。浏览器由于在 函数式组件中没法直接使用生命周期,就必须托管
Hook
来进行管理使用了。缓存
useEffect
可使用的 3 个生命周期函数:性能优化❞
componentDidmount
componentDidUpdate
componentWillUnmount
无需清除Effect
使用
❝什么是无需清除
Effect
使用?❝❞「React 更新 DOM 以后运行一些额外的代码」微信
那么它就是在生命周期的
❞compoentDidmount
和componentUpdate
中执行便可。
useEffect(() => {
//默认会执行
// 这块至关于 class 组件 生命周期的
//compoentDidmount compoentDidUpdate
}, [])
清除Effect
使用
❝1. 什么是 清除
Effect
?❝当组件进行卸载时,须要执行某些事件处理时,就须要用到 class 组件生命周期的
componentUnmount
.在
❞useEffect
中很方便使用,在内部返回一个方法便可,在方法中写相应业务逻辑2. 为何 要在
Effect
中返回一个函数 ?❝❞这是 effect 可选的清除机制。每一个 effect 均可以返回一个清除函数。如此能够将添加和移除订阅的逻辑放在一块儿。它们都属于 effect 的一部分。
❞
useEffect(()=>{
return () => {
console.log('组件卸载时执行')
}
})
监听 state
变化
❝能够经过控制 监听
❞state
变化来实现相应的业务逻辑。
useEffect(() => {
// 监听num,count 状态变化
// 不监听时为空 [] , 或者不写
}, [num, count])
完整栗子
import { useState, useEffect } from 'react';
export default () => {
const [num, setNum] = useState(0)
const [count, setCount] = useState(1)
useEffect(() => {
//默认会执行
// 这块至关于 class 组件 生命周期的 compoentDidmount compoentDidUpdate
console.log(`num: ${num}`)
console.log(`count: ${count}`)
// 组件在卸载时,将会执行 return 中内容
return () => {
// 至关于 class 组件生命周期的 componentWillUnMount
console.log('测试')
}
}, [num])
return (
<div>
<h1>{num}</h1>
<button onClick={() => { setNum(num + 1) }}> 更新Num</button>
<hr />
<h1>{count}</h1>
<button onClick={() => { setCount(count + 1) }}> 更新Count</button>
</div>
)
}
useRef
什么是 useRef
?
❝
useRef
返回的是一个可变的ref对象,它的属性current被初始化为传入的参数(initialValue),「返回的ref对象在组件的整个生命周期内保持不变」。做用:
❞
获取Dom操做,例如 获取
input
焦点获取子组件的实例(只有类组件可用)
在函数组件中的一个全局变量,不会由于重复 render 重复申明
栗子
import {useRef} from 'react';
export default () => {
const inputRef = useRef({value:0})
return (
<div>
<h1>测试</h1>
<input type="text" ref={inputRef} />
<button onClick={()=>{console.log(inputRef.current.value)}}>获取input 值</button>
<button onClick={()=>{inputRef.current.focus()}}>获取input 焦点</button>
</div>
)
}
useContext
状态数据共享
Context
解决了什么
❝在平常开发中,咱们父子组件都是经过
props
来进行通讯,若是遇到跨级组件通讯
那么咱们就很差经过props
来处理了。这时候能够想一想怎么能够把 组件 状态 共享出去使用?
Context
Redux
.....
本小节经过
❞Context
来 达到组件数据共享
什么是 Context
❝数据共享,任何组件均可访问Context 数据。
在
React
中,组件数据经过prop
来达到 自上而下的传递数据,要想实现全局传递数据,那么可使用Context
.注意:
Context 主要应用场景在于不少不一样层级的组件须要访问一样一些的数据。请谨慎使用,由于这会使得组件的复用性变差。
❞
建立 Context
❝在使用
❞Context
前提,必须建立它,能够为它单首创建一个文件来管理Context
,
import React from 'react';
export const MyContext = React.createContext();
使用 Context
❝在使用
Context
时,它一般用在顶级组件(父组件上),它包裹的内部组件均可以享受到state
的使用和修改。经过
Context.Provider
来进行包裹,值经过value = {}
传递。子组件如何使用
Context
传递过来的值 ?❞
经过 useContext()
Hook 能够很方便的拿到对应的值.
// Context.js
import React from 'react';
export const MyContext = React.createContext();
import { useContext } from 'react';
import {MyContext} from '../Context/index'
const result = {
code:200,
title:'添加数据成功'
}
const Son = () => {
const res = useContext(MyContext)
return (
<>
<div>
<h1>{res.code}</h1>
<hr/>
<h2>{res.title}</h2>
</div>
</>
)
}
export default () => {
return (
<MyContext.Provider value={result}>
<div>
<h1>前端自学社区</h1>
<Son/>
</div>
</MyContext.Provider>
)
}
useMemo
提高性能优化
定义
❝
useMemo
用于性能优化,经过记忆值来避免在每一个渲染上执⾏高开销的计算。
useMemo
参数:
useMemo
返回值是memoized
值,具备缓存做用array
控制useMemo
从新执⾏的数组,array 中 的 state 改变时
才会 从新执行useMemo
注意:
❞
不传数组,每次更新都会从新计算
空数组,只会计算一次
依赖对应的值,当对应的值发生变化时,才会从新计算(能够依赖另一个 useMemo 返回的值)
栗子
import { useState, useMemo} from 'react';
export default () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const newValue = useMemo(()=>{
console.log(`count 值为${count}`)
console.log(`num 值为 ${num}`)
return count+num
},[count])
return(
<div>
<h1>{count}</h1>
<button onClick={()=>{setCount(count+1)}}>count + 1</button>
<hr/>
<h1>{num}</h1>
<button onClick={()=>{setNum(num+1)}}>Num + 1</button>
<hr/>
<h2>{newValue}</h2>
</div>
)
}
解析栗子
❝当点击了 5 次更新
num
值,页面中newValue
的值始终显示为 0,这是为何呢?由于我在
useMemo
监听记录的是count
的值,当count
值发生变化时,页面上的newValue
在会从新计算,虽然你点击了 5 次 更新num
,页面没有更新,可是已经缓存起来了,当点击 更新count
时,它会 计算count+1 的值 和 num 缓存的值
, 最终结果 为 5。减小了计算消耗。
❞
useCallback
提高性能优化
定义
❝
useCallback
能够说是useMemo
的语法糖,能用useCallback
实现的,均可以使用useMemo
, 经常使用于react的性能优化。
useCallback
的参数:❞
callback
是一个函数用于处理逻辑array
控制useCallback
从新执⾏的数组,array改变时
才会从新执⾏useCallback
使用
❝它的使用和
❞useMemo
是同样的,只是useCallback
返回的函数。
import { useState, useCallback} from 'react';
export default () => {
const [count, setCount] = useState(0)
const [num, setNum] = useState(0)
const newValue = useCallback(()=>{
console.log(`count 值为${count}`)
console.log(`num 值为 ${num}`)
return count+num
},[count])
return(
<div>
<h1>{count}</h1>
<button onClick={()=>{setCount(count+1)}}>count + 1</button>
<hr/>
<h1>{num}</h1>
<button onClick={()=>{setNum(num+1)}}>Num + 1</button>
<hr/>
{/* 调用useCallback 返回的值 */}
<h2>{newValue()}</h2>
</div>
)
}
小结
❝
useMemo
和useCallback
功能相似,都是提高性能优化。该采用哪一种方式来最佳实践,还有待探索。
欢迎 读者 与 我交流。
网上对
useMemo
和useCallback
的见解 ?❝
useCallback
若是在函数式组件中的话,确实应该看成最佳实践来用,可是使用它的目的除了要缓存依赖未改变的回调函数以外(与 useMemo 相似),还有一点是为了可以在依赖发生变动时,可以确保回调函数始终是最新的实例,从而不会引起一些意料以外的问题,我感受后者才是使用 useCallback 的出发点,而非缓存。由于你想啊,即便不用 useCallback,假设这个回调函数也没有任何依赖状态,我直接把这个函数声明在组件外部不也能够吗?我直接使用 ref 不是更自由吗?❞
useMemo
自己名字就是和缓存有关联的,本质上就为了解决一个事情,在 render 里面不要直接建立对象或者方法什么的,由于组件每渲染一次,就会建立一次(好比 style 或者一些常量状态),形成没必要要的资源浪费。理想状况应当是,若是存在依赖,只在依赖变化时从新建立,不存在依赖,那就只建立一次。表面上看,若是全部状态都用 useMemo,确定没什么问题,但你还需从缓存的代价上来分析这个问题,若是使用 useMemo 缓存一个状态的代价大于它带来的优点,那是否是反而拔苗助长了?你们对
❞useMemo
和useCallback
有何见解,欢迎在下方评论或者加我讨论。
useImperativeHandle
定义
❝
useImperativeHandle
可让你在使用ref
时自定义暴露给父组件的实例值。在大多数状况下,应当避免使用ref
这样的命令式代码。useImperativeHandle
应当与forwardRef
一块儿使用。
useImperativeHandle
做用 :❝子组件能够暴露给父组件 实例使用
❞
格式: useImperativeHandle(ref,()=>{},[])
❞
参数1:子组件向父组件暴露的实例
参数2:函数,传递的父组件可操做的实例和方法
参数3:监听状态,更新状态
import {useState,useImperativeHandle, forwardRef,useRef} from 'react';
const Son = forwardRef( (props,ref) => {
const inputRef = useRef(0)
const domRef = useRef()
const [state, setState] = useState('等待')
useImperativeHandle(ref,()=>({
focus:() => {inputRef.current.focus()},
domRef
}))
return (
<div>
<h1>{state}</h1>
<hr/>
<input type="text" ref={inputRef}/>
<h2 ref={domRef}>测试---------useImperativeHandle</h2>
</div>
)
})
export default () => {
const refFather = useRef(0)
return (
<div>
<h1>父组件</h1>
<Son ref={refFather} />
<button onClick={()=>{refFather.current.focus()}}>获取子组件实例------获取input焦点</button>
<button onClick={()=>{console.log(refFather.current.domRef.current.innerHTML)}}>获取子组件实例------获取h2 Dom</button>
</div>
)
}
useReducer
定义
❝它是
useState
的替代方案。它接收一个形如(state, action) => newState
的reducer
,并返回当前的state
以及与其配套的dispatch
方法。若是熟悉
❞Redux
使用的话,用useReducer
就是轻车熟路了,发车了。
使用Reducer
实现一个加减器
import {useReducer} from 'react';
export default () => {
const [state, dispatch] = useReducer((state,action)=> {
switch (action.type){
case 'addNum':
return {
num:state.num+1
}
case 'subtractNum':
return {
num:state.num-1
}
}
},{
num:0
})
return (
<div>
<h2>{state.num}</h2>
<button onClick={()=>{dispatch({type:'addNum'})}}> 增长num</button>
<button onClick={()=>{dispatch({type:'subtractNum'})}}> 增长num</button>
</div>
)
}
结语
关注公众号 “前端自学社区”,便可获取更多前端高质量文章!
关注后回复关键词“加群”, 便可加入 “前端自学交流群”,共同窗习进步。
关注后添加我微信拉你进技术交流群
本文分享自微信公众号 - 前端自学社区(gh_ce69e7dba7b5)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。