共计 5841 个字符,预计需要花费 15 分钟才能阅读完成。
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 到最终获取到文件的过程。其中牵扯到浏览器架构、操作系统、网络等一系列常识。本文将从各个角度具体阐述这一过程,波及广度与深度。如果您是曾经有肯定根底的同学,那么本文能够疾速带你系统化整顿碎片化常识。