从Java到全栈,开发带SKU的真实企业级电商项目(附赠整套UI框架,配套升级Vue3.0内容)
超清原画 完整无密 包括所有视频课件以及源码
点击下崽:网盘链接
入门React Hooks及其常用的几个钩子函数
写在前面
React Hooks 是 React 团队在两年前的 16.8 版本推出的一套全新的机制。作为最主流的前端框架,React 的 API 非常稳定,这次更新的发布,让众多恐惧新轮子的前端大佬们虎躯一震,毕竟每一次更新又是高本钱的学习,这玩意到底好使么?
答案是好用的,关于 React 的开发者而言,只是多了一个选择。过去的开发方式是基于Class组件的,而hooks是基于函数组件,这意味着 这两种开发方式能够并存 ,而新的代码能够依据详细状况采用 Hooks 的方式来完成就行了。这篇文章主要就来引见一下 Hooks 的优势 和 常用的几个钩子函数 。
Hooks的优势
1.类组件的缺乏
代码量多 :
相较于函数组件的写法,运用类组件代码量要略多一点,这个是最直观的感受。
this指向:
类组件中总是需求思索this的指向问题,而函数组件则能够疏忽。
趋向复杂难以维护 :
在高版本的React中,又更新了一些生命周期函数,由于这些函数相互解耦,很容易形成分散不集中的写法,漏掉关键逻辑和多了冗余逻辑,招致后期debug艰难。相反,hooks能够把关键逻辑都放在一同,不显得那么割裂,调试起来也易懂一点。
状态逻辑难复用 :
在组件之间复用状态逻辑很难,可能要用到 render props (渲染属性)或者 HOC (高阶组件),但无论是渲染属性,还是高阶组件,都会在原先的组件外包裹一层父容器(普通都是 div 元素),招致层级冗余。
- Hooks带来的益处
逻辑复用
在组件之前复用状态逻辑,常常需求借助高阶组件等复杂的设计形式,这些高级组件会产生冗余的组件节点,让调试变得艰难,下面用一个demo来比照一下两种完成方式。
Class
在class组件场景下,定义了一个高阶组件,担任监听窗口大小变化,并将变化后的值作为 props 传给下一个组件。
const useWindowSize = Component => {
// 产生一个高阶组件 HOC,只包含监听窗口大小的逻辑
class HOC extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
size: this.getSize()
};
}
componentDidMount() {
window.addEventListener("resize", this.handleResize);
}
componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}
getSize() {
return window.innerWidth > 1000 ? "large" :"small";
}
handleResize = ()=> {
const currentSize = this.getSize();
this.setState({
size: this.getSize()
});
}
render() {
// 将窗口大小传送给真正的业务逻辑组件
return ;
}
}
return HOC;
};
复制代码
接下来能够在自定义组件中能够调用 useWindowSize 这样的函数来产生一个新组件,并自带 size 属性,例如:
class MyComponent extends React.Component{
render() {
const { size } = this.props;
if (size === "small") return ;
else return ;
}
}
// 运用 useWindowSize 产生高阶组件,用于产生 size 属性传送给真正的业务组件
export default useWindowSize(MyComponent);
复制代码
下面看下Hooks的完成方式
Hooks
const getSize = () => {
return window.innerWidth > 1000 ? "large" : "small";
}
const useWindowSize = () => {
const [size, setSize] = useState(getSize());
useEffect(() => {
const handler = () => {
setSize(getSize())
};
window.addEventListener('resize', handler);
return () => {
window.removeEventListener('resize', handler);
};
}, []);
return size;
};
复制代码
运用:
const Demo = () => {
const size = useWindowSize();
if (size === "small") return ;
else return ;
};
复制代码
从上面的例子中经过 Hooks 的方式对窗口大小停止了封装,从而将其变成一个可绑定的数据源。这样当窗口大小发作变化时,运用这个 Hook 的组件就都会重新渲染。而且代码也愈加简约和直观,不会产生额外的组件节点,也不显得那么冗余了。
业务代码愈加聚合
下面举一个最常见的计时器的例子。
class
let timer = null
componentDidMount() {
timer = setInterval(() => {
// ...
}, 1000)
}
// ...
componentWillUnmount() {
if (timer) clearInterval(timer)
}
复制代码
Hooks
useEffect(() => {
let timer = setInterval(() => {
// ...
}, 1000)
return () => {
if (timer) clearInterval(timer)
}
}, [//...])
复制代码
Hooks的完成方式能让代码愈加集中,逻辑也更明晰。
写法简约
这个就不举例了,能够从字面意义了解,运用函数组件的确能少些很多代码,懂得都懂,嘻嘻~
几个内置Hooks的作用以及运用考虑
useState :让函数组件具有维持状态的才能
const[count, setCount]=useState(0);
复制代码
优点:
让函数组件具有维持状态的才能,即:在一个函数组件的屡次渲染之间,这个 state 是共享的。便于维护状态。
缺陷:
一旦组件有本人状态,意味着组件假如重新创立,就需求有恢复状态的过程,这通常会让组件变得更复杂。
用法:
useState(initialState) 的参数 initialState 是创立 state 的初始值。
它能够是恣意类型,比方数字、对象、数组等等。
useState() 的返回值是一个有着两个元素的数组。第一个数组元素用来读取 state 的值,第二个则是用来设置这个 state 的值。
在这里要留意的是,state 的变量(例子中的 count)是只读的,所以我们必需经过第二个数组元素 setCount 来设置它的值。
假如要创立多个 state ,那么我们就需求屡次调用 useState。
什么样的值应该保管在 state 中?
通常来说,我们要遵照的一个准绳就是:state 中不要保管能够经过计算得到的值 。
从 props 传送过来的值。有时分 props 传送过来的值无法直接运用,而是要经过一定的计算后再在 UI 上展现,比方说排序。那么我们要做的就是每次用的时分,都重新排序一下,或者应用某些 cache 机制,而不是将结果直接放到 state 里。
从 URL 中读到的值。比方有时需求读取 URL 中的参数,把它作为组件的一局部状态。那么我们能够在每次需求用的时分从 URL 中读取,而不是读出来直接放到 state 里。
从 cookie、localStorage 中读取的值。通常来说,也是每次要用的时分直接去读取,而不是读出来后放到 state 里。
useEffect:执行反作用
useEffect(fn, deps);
useEffect ,望文生义,用于执行一段反作用。
什么是反作用?
通常来说,反作用是指一段和当前执行结果无关的代码。比方说要修正函数外部的某个变量,要发起一个恳求,等等。
也就是说,在函数组件的当次执行过程中, useEffect 中代码的执行是不影响渲染出来的 UI 的。
对应到 Class 组件,那么 useEffect 就涵盖了 ComponentDidMount、componentDidUpdate 和 componentWillUnmount 三个生命周期办法。不过假如你习气了运用 Class 组件,那千万不要依照把 useEffect 对应到某个或者某几个生命周期的办法。你只需记住,useEffect 是每次组件 render 完后判别依赖并执行就能够了。
useEffect 还有两个特殊的用法:没有依赖项,以及依赖项作为空数组。我们来详细剖析下。
没有依赖项,则每次 render 后都会重新执行。例如:
useEffect(() => {
// 每次 render 完一定执行
console.log('渲染...........');
});
复制代码
空数组作为依赖项,则只在初次执行时触发,对应到 Class 组件就是 componentDidMount。例如:
useEffect(() => {
// 组件初次渲染时执行,等价于 class 组件中的 componentDidMount
console.log('did mount........');
}, []);
复制代码
小结用法:
总结一下,useEffect 让我们可以在下面四种机遇去执行一个回调函数产生反作用:
每次 render 后执行:不提供第二个依赖项参数。
比方useEffect(() => {})。
仅第一次 render 后执行:提供一个空数组作为依赖项。
比方useEffect(() => {}, [])。
第一次以及依赖项发作变化后执行:提供依赖项数组。
比方useEffect(() => {}, [deps])。
组件 unmount 后执行:返回一个回调函数。
比方useEffect() => { return () => {} }, [])。
useCallback:缓存回调函数
useCallback(fn, deps)
为什么要运用useCallback?
在 React 函数组件中, 每一次 UI 的变化,都是经过重新执行整个函数来完成的 ,这和传统的 Class 组件有很大区别:函数组件中并没有一个直接的方式在屡次渲染之间维持一个状态。
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = () => setCount(count+1);
return +
}
复制代码
考虑下这个过程。 每次组件状态发作变化的时分,函数组件实践上都会重新执行一遍 。在每次执行的时分,实践上都会创立一个新的事情处置函数 handleIncrement 。
这也意味着,即便 count 没有发作变化,但是函数组件由于其它状态发作变化而重新渲染时(函数组件重新被执行),这种写法也会每次创立一个新的函数。创立一个新的事情处置函数,固然不影响结果的正确性,但其实是没必要的。由于这样做不只增加了系统的开支,更重要的是: 每次创立新函数的方式会让接纳事情处置函数的组件,需求重新渲染 。
比方这个例子中的 button 组件,接纳了 handleIncrement ,并作为一个属性。假如每次都是一个新的,那么这个 React 就会以为这个组件的 props 发作了变化,从而必需重新渲染。因而,我们需求做到的是: 只要当 count 发作变化时,我们才需求重新定一个回调函数 。而这正是 useCallback 这个 Hook 的作用。
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = useCallback(
() => setCount(count + 1),
[count], // 只要当 count 发作变化时,才会重新创立回调函数
);
return +
}