Hooks
Hooks
是 React
16.8 的新增个性,可能在不写 class
的状况下应用 state
以及其余个性。
动机
- 在组件之间复用状态逻辑很难
- 简单组件变得难以了解
难以了解的
class
Hooks
规定- 只有在最顶层应用
Hooks
不要再循环/条件/嵌套函数中应用` 只有在
React
函数中调用Hooks
函数组件和类组件的不同
函数组件可能捕捉到以后渲染的所用的值。
点击查看示例
对于类组件来说,尽管 props
是一个不可变的数据,然而 this
是一个可变的数据,在咱们渲染组件的时候 this
产生了扭转,所以 this.props
产生了扭转,因而在 this.showMessage
中会拿到最新的 props
值。
对于函数组件来说捕捉了渲染所应用的值,当咱们应用 hooks
时,这种个性也同样的试用于 state
上。
点击查看示例
const showMessage = () => { alert("写入:" + message);};const handleSendClick = () => { setTimeout(showMessage, 3000);};const handleMessageChange = (e) => { setMessage(e.target.value);};
如果咱们想跳出'函数组件捕捉以后渲染的所用值‘这个个性,咱们能够采纳 ref
来追踪某些数据。通ref.current
能够获取到最新的值
const showMessage = () => { alert("写入:" + ref.current);};const handleSendClick = () => { setTimeout(showMessage, 3000);};const handleMessageChange = (e) => { setMessage(e.target.value); ref.current = e.target.value;};
useEffect
useEffect 可能在函数组件中执行副作用操作(数据获取/波及订阅),其实能够把 useEffect
看作是 componentDidMount / componentDidUpdate / componentWillUnMount
的组合
- 第一个参数是一个
callback
,返回destory
。destory
作为下一个callback
执行前调用,用于革除上一次callback
产生的副作用 - 第二个参数是依赖项,一个数组,能够有多个依赖项。依赖项扭转,执行上一个
callback
返回的destory
,和执行新的effect
第一个参数callback
对于 useEffect
的执行,React
解决逻辑是采纳异步调用
的,对于每一个 effect
的 callback
会像 setTimeout
回调函数一样,放到工作队列外面,等到主线程执行结束才会执行。所以 effect
的回调函数不会阻塞浏览器绘制视图
相干的生命周期替换计划
- componentDidMount 代替计划
React.useEffect(()=>{//申请数据,事件监听,操纵DOM},[]) //dep=[],只有在初始化执行/* 因为useEffect会捕捉props和state, 所以即便是在回调函数中咱们拿到的还是最后的props和state*/
componentDidUnmount
代替计划
React.useEffect(()=>{/* 申请数据 , 事件监听 , 操纵dom , 减少定时器,延时器 */return function componentWillUnmount(){ /* 解除事件监听器 ,革除定时器,延时器 */}},[])/* 切记 dep = [] *///useEffect第一个函数的返回值能够作为componentWillUnmount应用
componentWillReceiveProps
代替计划
其实两者的执行机会是齐全不同的,一个在
render
阶段,一个在commit
阶段,useEffect
会初始化执行一次,然而componentWillReceiveProps
只会在props
变动时执行更新React.useEffect(()=>{console.log('props变动:componentWillReceiveProps')},[ props ])
componentDidUpdate
代替计划
useEffect
和componentDidUpdate
在执行期间尽管有点差异,useEffect
是异步执行,componentDidUpdate
是同步执行 ,但都是在commit
阶段React.useEffect(()=>{console.log('组件更新实现:componentDidUpdate ') }) //没有dep依赖项,没有第二个参数,那么每一次执行函数组件,都会执行该 effect。
- 在
useEffect
中[]须要解决什么
React 官网 FAQ这样说:
只有当函数(以及它所调用的函数)不援用 props
、state
以及由它们衍生而来的值时,你能力释怀地把它们从依赖列表中省略,应用 eslint-plugin-react-hooks
帮忙咱们的代码做一个校验
点击查看具体示例
function Counter() { const [count, setCount] = useState(0); useEffect(() => { const id = setInterval(() => { setCount(count + 1); }, 1000); return () => clearInterval(id); }, []); return <h1>{count}</h1>;}//只会做一次更新,而后定时器不再转动
- 是否应该把函数当做
effect
的依赖
const loadResourceCatalog = async () => { if (!templateType) return const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog' const res: any = await API[reqApi]() if (!res.success) return setCatalog(res.data)}useEffect(() => { loadResourceCatalog();}, [])//在函数loadResourceCatalog中应用了templateType这样的一个state//在开发的过程中可能会遗记函数loadResourceCatalog依赖templateType值
第一个简略的解法,对于某些只在 useEffect 中应用的函数,间接定义在 effect
中,以至于可能间接依赖某些 state
useEffect(() => { const loadResourceCatalog = async () => { if (!templateType) return const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog' const res: any = await API[reqApi]() if (!res.success) return setCatalog(res.data) } loadResourceCatalog();}, [templateType])
如果咱们须要在很多中央用到咱们定义的函数,不可能把定义放到以后的 effect
中,并且将函数放到了第二个的依赖参数中,那这个代码将就进入死循环。因为函数在每一次渲染中都返回一个新的援用
const Template = () => { const getStandardTemplateList = async () => { const res: any = await API.getStandardTemplateList() if (!res.success) return; const { data } = res; setCascaderOptions(data); getDefaultOption(data[0]) } useEffect(()=>{ getStandardTemplateList() }, [])}
针对这种状况,如果以后函数没有援用任何组件内的任何值,能够将该函数提取到组件里面去定义,这样就不会组件每次 render
时不会再次扭转函数援用。
const getStandardTemplateList = async () => { const res: any = await API.getStandardTemplateList() if (!res.success) return; const { data } = res; setCascaderOptions(data); getDefaultOption(data[0])}const Template = () => { useEffect(()=>{ getStandardTemplateList() }, [])}
如果说以后函数中援用了组件内的一些状态值,能够采纳 useCallBack
对以后函数进行包裹
const loadResourceCatalog = useCallback(async () => { if (!templateType) return const reqApi = templateType === TEMPLATE_TYPE.STANDARD ? 'listCatalog' : 'getCodeManageCatalog' const res: any = await API[reqApi]() if (!res.success) return setCatalog(res.data)}, [templateType])useEffect(() => { loadResourceCatalog();}, [loadResourceCatalog])//通过useCallback的包裹,如果templateType放弃不变,那么loadResourceCatalog也会放弃不变,所以useEffect也不会从新运行//如果templateType扭转,那么loadResourceCatalog也会扭转,所以useEffect也会从新运行
useCallback
React 官网定义
useCallback(fn, deps)
返回一个 memoized
回调函数,该回调函数仅在某个依赖项扭转时才会更新
import React, { useCallback, useState } from "react";const CallBackTest = () => { const [count, setCount] = useState(0); const [total, setTotal] = useState(0); const handleCount = () => setCount(count + 1); //const handleCount = useCallback(() => setCount(count + 1), [count]); const handleTotal = () => setTotal(total + 1); return ( <div> <div>Count is {count}</div> <div>Total is {total}</div> <div> <Child onClick={handleCount} label="Increment Count" /> <Child onClick={handleTotal} label="Increment Total" /> </div> </div> );};const Child = React.memo(({ onClick, label }) => { console.log(`${label} Child Render`); return <button onClick={onClick}>{label}</button>;});export default CallBackTest;
点击查看具体示例
React.memo
是通过记忆组件渲染后果的形式来进步性能,memo
是 react16.6
引入的新属性,通过浅比拟
(源码通过 Object.is
办法比拟)以后依赖的 props
和下一个 props
是否雷同来决定是否从新渲染;如果应用过类组件形式,就能晓得 memo
其实就相当于 class
组件中的 React.PureComponent
,区别就在于 memo
用于函数组件。useCallback
和 React.memo
肯定要联合应用能力有成果。
应用场景
- 作为
props
,传递给子组件,为防止子元素不必要的渲染,须要配合React.Memo
应用,否则无意义 作为
useEffect
的依赖项,须要进行比拟的时候才须要加上useCallback
useMemo
React 官网定义
返回一个 memoized
值
仅会在某个依赖项扭转时才从新计算 memoized
值,这种优化有助于防止在每次渲染时都进行高开销的计算 useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
,对于实现上,基本上是和 useCallback
类似,只是稍微有些不同
应用场景
- 防止在每次渲染时都进行高开销的计算
两个 hooks
内置于 React
都有特地的起因:
1.援用相等
当在 React
函数组件中定义一个对象时,它跟上次定义的雷同对象,援用是不一样的(即便它具备所有雷同值和雷同属性)
- 依赖列表
React.memo
大多数时候,你不须要思考去优化不必要的从新渲染,因为优化总会带来老本。
- 低廉的计算
计算成本很高的同步计算值的函数
总结
本文介绍了 hooks
产生动机、函数组件和类组件的区别以及 useEffect / useCallback / useMemo
等内容。重点介绍了 useEffect
的生命周期替换计划以及是否把函数作为 useEffect
的第二参数。
参考链接
When to useMemo and useCallback
How to fetch data with React Hooks
A Complete Guide to useEffect
How Are Function Components Different from Classes?
useCallback、useMemo 剖析 & 差异
网络的导航,是从输出 url 到最终获取到文件的过程。其中牵扯到浏览器架构、操作系统、网络等一系列常识。本文将从各个角度具体阐述这一过程,波及广度与深度。如果您是曾经有肯定根底的同学,那么本文能够疾速带你系统化整顿碎片化常识。