当前位置: 首页 > 后端技术 > Java

GeekJava进阶训练营结束了

时间:2023-04-01 15:30:05 Java

GeekJava进阶训练营结束了。超清原画完整未分类,包括所有视频课件和源码。点此:网盘链接入门ReactHooks和几个常用的钩子函数编写前面,ReactHooks是React团队在两年前的16.8版本中引入的全新机制。作为最主流的前端框架,React的API非常稳定。此次更新的发布,震惊了很多怕新轮子的前端大佬们。毕竟每一次更新都是一次高成本的学习。什么?答案很简单,对于React开发者来说,只是多了一个选择。过去的开发方式是基于Class组件,而hooks是基于函数组件,这意味着两种开发方式可以并存,新的代码可以根据具体情况以Hooks的形式完成。本文主要介绍Hooks的优点和几个常用的钩子函数。Hooks的优点1.类组件少,代码量大:相对于函数组件的写法,类组件的使用需要多一点代码,这是最直观的感受。this指向:class组件总是需要考虑this的指向,而function组件可以忽略。它趋于复杂且难以维护:在更高版本的React中,更新了一些生命周期函数。由于这些功能相互解耦,很容易形成分散、不集中的写法,漏掉关键逻辑,增加冗余逻辑,导致后期调试。难的。相反,hooks可以把所有的关键逻辑放在一起,不那么零散,调试的时候更容易理解。状态逻辑复用困难:组件之间状态逻辑复用困难。Renderprops(渲染属性)或者HOC(高阶组件)都可能用到,但是无论是渲染属性还是高阶组件,都会在原来的父容器(一般是div元素)包裹组件,导致分层冗余。Hooks逻辑复用带来的好处复用组件之前的状态逻辑,往往需要用到高层组件等复杂的设计形式,这些高层组件会产生冗余的组件节点,给调试带来困难,我们用一个demo来说明一下比较一下两种完成方法。Class在类组件场景中定义了一个高层组件,负责监听窗口大小的变化,并将变化的值作为props传递给下一个组件。constuseWindowSize=Component=>{//生成一个高阶组件HOC,只包含监控窗口大小的逻辑classHOCextendsReact.PureComponent{constructor(props){super(props);this.state={size:this.getSize()};}componentDidMount(){window.addEventListener("resize",this.handleResize);}componentWillUnmount(){window.removeEventListener("resize",this.handleResize);}getSize(){返回window.innerWidth>1000?“大”:“小”;}handleResize=()=>{constcurrentSize=this.getSize();this.setState({size:this.getSize()});}render(){//将窗口大小发送给真正的业务逻辑组件return;}}returnHOC;};复制代码后,可以在自定义组件中调用useWindowSize等函数生成一个新的组件,该组件自带size属性,例如:classMyComponentextendsReact.Component{render(){const{size}=this.props;if(size==="small")return;elsereturn;}}//使用useWindowSize生成高层组件,用于生成size属性传递给真正的业务组件exportdefaultuseWindowSize(MyComponent);复制下面的代码看看Hooks是如何完成的HooksconstgetSize=()=>{返回window.innerWidth>1000?“大”:“小”;}constuseWindowSize=()=>{const[size,setSize]=useState(getSize());useEffect(()=>{consthandler=()=>{setSize(getSize())};window.addEventListener('resize',handler);return()=>{window.removeEventListener('resize',handler);};},[]);返回大小;};复制代码使用:constDemo=()=>{constsize=useWindowSize();如果(大小===“小”)返回;否则返回;};从上面的例子中复制代码,通过Hooks方法封装窗口大小,从而将其变成一个可绑定的数据源,这样当窗口大小发生变化时,使用这个Hook的组件就会重新渲染。而且代码更加简洁直观,不会额外生成组件节点,也不会显得那么冗余。业务代码越来越聚合。这是最常见的计时器的示例。classlettimer=nullcomponentDidMount(){timer=setInterval(()=>{//...},1000)}//...componentWillUnmount(){if(timer)clearInterval(timer)}复制代码HooksuseEffect(()=>{lettimer=setInterval(()=>{//...},1000)return()=>{if(timer)clearInterval(timer)}},[//...])Hooks实现方法可以让代码更浓缩,逻辑更清晰。这种简单的写法我就不举例了。从字面意思就可以理解,使用函数组件确实可以节省很多代码,我都明白了。const[count,setCount]=useState(0);复制代码的好处:让功能组件有状态维护的能力,即状态在一个功能组件的多次渲染之间共享。易于维护状态。缺点:一旦组件有了自己的状态,就意味着如果重新创建组件,需要有一个恢复状态的过程,这通常会使组件变得更加复杂。用法:useState(initialState)的参数initialState是创建状态的初始值。它可以是任何类型,例如数字、对象、数组等。useState()的返回值是一个包含两个元素的数组。第一个数组元素用于读取状态的值,第二个用于设置状态的值。这里需要注意的是状态变量(例子中的count)是只读的,所以我们必须通过第二个数组元素setCount来设置它的值。如果我们要创建多个状态,那么我们需要多次调用useState。应该保持什么样的价值观?一般来说,我们要遵循的一个原则是:不保存状态中可以计算的值。从道具传递的价值。props传递的值有时不能直接使用,必须在UI上计算后再显示,比如排序。那么我们要做的就是每次使用的时候重新排序,或者使用一些缓存机制,而不是直接把结果放在state中。从URL读取的值。例如,有时需要读取URL中的参数并将其作为组件状态的一部分。那么我们每次需要的时候都可以从URL中读取,而不是读出来直接放到state中。从cookie、localStorage读取的值。一般来说也是每次用到的时候直接读,而不是读出来放到状态。useEffect:执行一个反应useEffect(fn,deps);useEffect,顾名思义,就是用来做反应的。什么是反应?一般来说,反应是指一段与当前执行结果无关的代码。比如你想在函数外修改一个变量,你想发出请求等等。也就是说,在函数组件当前执行过程中,useEffect中代码的执行不会影响渲染的UI。对应Class组件,那么useEffect涵盖了ComponentDidMount、componentDidUpdate和componentWillUnmount这三个生命周期方法。但是如果你习惯使用Class组件,那么就不要按照useEffect映射到一个或几个生命周期的方法。你只需要记住useEffect就是判断依赖关系,在每次组件渲染的时候执行。useEffect还有两个特殊的用法:无依赖,依赖为空数组。让我们详细分解一下。如果没有依赖项,它将在每次渲染后重新执行。例如:useEffect(()=>{//每次渲染结束,都要执行console.log('rendering..........');});把代码复制空数组作为依赖,然后只在第一次执行时触发,对应的Class组件是componentDidMount。例如:useEffect(()=>{//在组件第一次渲染时执行,相当于类组件中的componentDidMountconsole.log('didmount.......');},[]);CopyCodeSummaryUsage:综上所述,useEffect允许我们在以下四种产生副作用的机会中执行回调函数:每次渲染后执行:不提供第二个依赖参数。例如useEffect(()=>{})。仅在第一次渲染后执行:提供一个空数组作为依赖项。例如useEffect(()=>{},[])。首次执行并在依赖项更改后执行:提供依赖项数组。例如useEffect(()=>{},[deps])。组件卸载后执行:返回回调函数。例如useEffect()=>{return()=>{}},[])。useCallback:缓存回调函数useCallback(fn,deps)为什么要用useCallback?在React函数组件中,每个UI更改都是通过重新执行整个函数来完成的,这与传统的Class组件有很大不同:函数组件没有直接的方法来维护渲染之间的状态。函数Counter(){const[count,setCount]=useState(0);consthandleIncrement=()=>setCount(count+1);return+}复制代码考虑这个过程。每次组件状态发生变化时,实际上都会重新执行功能组件。在每次执行时,实际上都会创建一个新的事件处理函数handleIncrement。这也就意味着,即使count没有发生变化,但是当函数组件因为其他状态改变而被重新渲染时(函数组件被重新执行),这种写法每次都会创建一个新的函数。新建一个事件处理函数不影响结果的正确性,但实际上是没有必要的。因为这样做不仅增加了系统的开销,更重要的是:每创建一个新的函数,需要重新渲染接受事件处理函数的组件。例如,此示例中的按钮组件接受handleIncrement作为属性。如果每次都是新的,那么这个React就会认为这个组件的props发生了变化,所以必须重新渲染。因此,我们需要做的是:只有当count发生变化时,我们才需要重新定义一个回调函数。而这正是useCallbackHook所做的。importReact,{useState,useCallback}from'react';functionCounter(){const[count,setCount]=useState(0);consthandleIncrement=useCallback(()=>setCount(count+1),[count],//只有当计数改变时,回调函数才会被重新创建);返回+}