0


React Hooks概述及常用的Hooks介绍

React Hooks概述及常用的Hooks介绍

1 为什么会有Hooks

React的组件创建方式,一种是类组件,一种是纯函数组件,并且React团队希望,组件不要变成复杂的容器,最好只是数据流的管道。开发者根据需要,组合管道即可。也就是说组件的最佳写法应该是函数,而不是类

函数组件和类组件的区别:
(1)函数组件没有状态(state),类组件有
(2)函数组件没有生命周期,类组件有(挂载、更新、销毁)
(3)函数组件没有this,类组件有
(4)函数组件更适合做UI展示,类组件更适合做复杂的业务逻辑组件

这就注定,函数组件更适合做UI展示的功能,涉及到状态的管理与切换,我们不得不用类组件或者redux。但我们知道类组件的也是有缺点的,比如,遇到简单的页面,代码会显得很重,并且每创建一个类组件,都要去继承一个React实例;至于Redux,更不用多说,很久之前Redux的作者就说过,“能用React解决的问题就不用Redux”。

例如,使用类组件做一个简单的计数器,它的具体代码如下:

import React from"react";classCountextendsReact.Component{constructor(props){super(props);this.state ={count:0// 设置初始状态为0}}addCount=()=>{// 让count加1let count =this.state.count;this.setState({count: count +=1})}render(){return(<div><p>{this.state.count}</p><button onClick={this.addCount}>加1</button></div>)}}

可以看出来,上面的代码确实很重。为了解决这种类组件功能齐全却很重,纯函数很轻便却有一些重大限制,React团队设计了React Hooks。

React Hooks就是加强版的函数组件,我们可以完全不使用class,就能写出一个全功能的组件。

2 Hooks的含义

“Hooks”的单词意思为“钩子”。React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。而React Hooks 就是我们所说的“钩子”。

那么Hooks要怎么用呢?“你需要写什么功能,就用什么钩子”。对于常见的功能,React为我们提供了一些常用的钩子,如果有特殊需要,我们也可以写自己的钩子。下面是React Hooks为我们提供的常用的钩子:

  • useState()
  • useEffect()
  • useCallback()
  • useMemo()
  • useRef()
  • useContext()
  • useReducer()

不同的钩子为函数引入不同的外部功能,上面四种钩子都带有use前缀,React Hooks约定,钩子一律使用

use

前缀命名。所以,自己定义的钩子都要命名为

useXXX

3 Hooks的用法

3.1 useState():状态钩子

纯函数组件没有状态,

useState()

用于设置和使用组件的状态属性。语法如下:

const[state, setState]=useState(initialValue);// state:初始的状态属性,指向状态当前值,类似this.state// setState:修改状态属性值的函数,用来更新状态,类似setState// initialValue:状态的初始值,该值会赋给state

注意:

setState

的命名为:

set+State(初始状态名)

,并且采用小驼峰命名法。例如

[count, setCount]

[name, setName]

示例:使用Hooks重写计数器

constCount=()=>{const[count, setCount]=useState(0);// 将0设置为count的初始值constaddCount=()=>{let newCount = count;setCount(newCount +=1);}return(<div><p>{count}</p><button onClick={addCount}>加1</button></div>)}

用函数组件实现了一个功能完全一样的计数器,代码看起来更加的轻便简洁,没有了继承,没有了渲染逻辑,没有了生命周期等。这就是hooks存在的意义。

3.2 useEffect():副作用钩子

useEffect()

是副作用的钩子,可以实现特定的功能,如异步请求。语法如下:

useEffect(()=>{// 回调函数,其中是要进行的异步操作代码},[array])// [array]:useEffect执行的依赖,当该数组的值发生改变时,回调函数中的代码就会被指向// 如果[array]省略,则表示不依赖,在每次渲染时回调函数都会执行// 如果[array]是空数组,即useEffect第二项为[],表示只执行一次

示例:通过

useEffect()

模拟异步加载数据(第二项省略)。

constAsyncPage=()=>{// 首先设置loading状态为trueconst[loading, setLoading]=useState(true);useEffect(()=>{// 2秒后将loading状态设置为falsesetTimeout(()=>{setLoading(false);},2000);})return(// 判断loading是否为true,是就显示loading,不是就显示异步请求完成
        loading ?<p>loading...</p>:<p>异步请求完成</p>)}

请添加图片描述
示例:

useEffect()

依赖第二项数组变化

constAsyncPage=({name})=>{const[loading, setLoading]=useState(true);// 设置loading状态为trueconst[person, setPerson]=useState({});// 设置person状态为空对象useEffect(()=>{// 首先设置loading为true,2秒后改为false,name改成传过来的参数setLoading(true);setTimeout(()=>{setLoading(false);setPerson({name});},2000);},[name]);// 表示当name修改才会执行回调函数return(<>{loading ?<p>Loading...</p>:<p>{person.name}</p>}</>)}constPersonPage=()=>{// 设置初始state为空字符串const[state, setState]=useState("");constchangeName=(name)=>{// 修改name的函数setState(name);}return(<>{/*首先将state传给name*/}<AsyncPage name={state}/><button onClick={()=>{// 点击按钮后将张三传给namechangeName("张三")}}>张三
            </button><button onClick={()=>{changeName("李四")}}>李四
            </button></>)}

请添加图片描述

useEffect和useLayoutEffect的区别

useEffect()

useLayoutEffect()

主要的区别是调用时机不同

useLayoutEffect()

componentDidMount()

componentDidUpate()

一致,再react完成DOM更新后马上同步调用代码,它会阻塞页面的渲染,而

useEffect()

则会在页面渲染完后才会异步调用。

在实际使用中如果项避免页面抖动,可以把需要操作DOM的代码放在

useLayoutEffect()

中,在该函数中做DOM操作,这些DOM修改会和react做出的更改一起被一次性渲染到屏幕上,只有一次回流重绘的代价。

3.3 useCallback():记忆函数

useCallback()

为记忆函数,它可以防止因为组件重新渲染,导致方法被重新创建,起到缓存作用。语法如下:

useCallback(()=>{// 回调函数,当array改变后,该函数才会重新声明},[array])// 如果[array]为空数组,那么就是第一次创建后就被缓存,如果后期array改变了,拿到的还是老的array// 如果不传入第二个参数,每次都会重新声明一次,拿到的就是最新的array

比如说下面一段代码中,我们可以看到有很多的函数,当我们在return中修改一个状态,就会导致整个页面重新渲染,那么这些函数(

handleChange1

handleChange2

…)也会被重新创建,这样会造成性能的浪费,因此可以使用

useCallback

将这些函数缓存下来,这样下一次页面重新渲染的时候,某些函数就不会重新被创建了。

constUseCallback=function(){consthandleChange1=()=>{// 具体代码}consthandleChange2=()=>{// 具体代码}consthandleChange3=()=>{// 具体代码}consthandleChange4=()=>{// 具体代码}return(<div>{/*具体代码*/}</div>)}

使用

useCallback()

时,只需要将其写在整个函数外部即可,上面代码使用

useCallback()

后效果如下,每当依赖项改变时,该函数才会被重新创建,如果依赖项不变,则不会重新创建。

constUseCallback=function(){const handleChange1 =useCallback(()=>{// 具体代码},[依赖项])const handleChange2 =useCallback(()=>{// 具体代码},[依赖项])const handleChange3 =useCallback(()=>{// 具体代码},[依赖项])const handleChange4 =useCallback(()=>{// 具体代码},[依赖项])return(<div>{/*具体代码*/}</div>)}

3.4 useMemo():记忆组件

useCallback()

的功能可以由

useMemo()

所替代,

useMemo()

也可以返回一个记忆函数,语法如下:

useMemo(()=> fn,[])// useCallback(fn, []) = useMemo(() => fn, [])
useCallback()

useMemo()

的区别:

useCallback()

不会执行第一个参数函数,而是将其返回,

useMemo()

会执行第一个函数并且将函数执行结果返回给你。

useCallback()

常用记忆时间按函数,生成记忆后的时间函数传递给子组件使用,

useMemo()

更适合经过函数计算得到一个确定的只,比如记忆组件。

3.5 useRef():保存引用值

useRef()

等价于类组件中的

React.createRef()

,语法如下:

const loginRef =useRef();

使用

useRef()

创建了一个值后,就可以将其绑定到DOM节点上,给DOM节点增加一个

ref

属性,将

loginRef

传入,则可以通过其

current

属性获取到DOM节点的值,语法如下:

<inputref={loginRef}/>

除此之外,我们都知道

useState()

可以保存一个状态,那么另一个方法就是使用

useRef()

,为

useRef()

传入一个初始值,它可以帮助我们记住这个状态。

3.6 useContext():共享状态钩子

useContext()可以共享状态,作用是进行状态的分发(React16.x以后的版本支持),避免了使用Props进行数据的传递。语法如下:

// 第一步:创建全局的Contextconst AppContext = React.createContext([初始化参数])// 第二步:通过全局的Context进行状态值的共享<AppContext.Provider value={{属性名: 值 }}><组件1/><组件2/></AppContext>

示例:A组件和B组件共享一个状态

constCount=()=>{const AppContext = React.createContext({});constA=()=>{const{name}=useContext(AppContext);return(<div>
                我是A组件,我的名字是:{name}</div>)}constB=()=>{const{name}=useContext(AppContext);return(<div>
                我是B组件,我的名字是:{name}</div>)}return(<AppContext.Provider value={{name:"橘猫吃不胖"}}><A/><B/></AppContext.Provider>)}

在这里插入图片描述

3.7 useReducer():Action钩子

在使用React的过程中,如遇到状态管理,一般会用到Redux,而React本身是不提供状态管理的。而

useReducer()

提供了状态管理。

useReducer()

useState()

的替代方案。首先,关于redux我们都知道,其原理是通过用户在页面中发起action,从而通过reducer方法来改变state,从而实现页面和状态的通信。而Reducer的形式是(state, action) => newstate,返回当前的 state 以及与其配套的 dispatch 方法。。Hooks的useReducer()是这样的:

const[state, dispatch]=useReducer(reducer, initialState)

它接受reducer函数和状态的初始值作为参数,返回一个数组,其中第一项为当前的状态值,第二项为发送action的dispatch函数。

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为可以向子组件传递 dispatch 而不是回调函数。

例如:使用useReducer()实现一个计数器

constCount=()=>{constreducer=(state, action)=>{if(action.type =="add"){return{...state,count: state.count +1}}else{return state
        }}constaddCount=()=>{dispatch({type:"add"})}const[state, dispatch]=useReducer(reducer,{count:0});return(<><p>{state.count}</p><button onClick={addCount}>加1</button></>)}

通过代码可以看到,使用useReducer()代替了Redux的功能,但useReducer无法提供中间件等功能,假如有这些需求,还是需要用到redux。

4 创建自己的Hooks

以上介绍了四种最常用的react提供给我们的默认React Hooks,有时候我们需要创建我们自己想要的Hooks,来满足更便捷的开发,就是根据业务场景对以上四种Hooks进行组装,从而得到满足自己需求的钩子。

用户自定义的Hooks:
A、命名的要求:用use开头,后跟名称(首字母大写)
B、作用:根据具体业务的需求,对Hooks中默认的钩子函数进行封装,使代码的结构更加清晰,便于使用和维护

示例:封装自己的Hooks

// 封装自己的HooksconstusePerson=({name})=>{const[loading, setLoading]=useState(true);const[person, setPerson]=useState({});useEffect(()=>{setLoading(true);setTimeout(()=>{setLoading(false)setPerson({name})},2000)},[name])//第二个参数为默认return[loading, person];}constAsyncPage=(name)=>{// 只进行UI展示const[loading, person]=usePerson(name)return(<>{loading ?<p>Loading...</p>:<p>{person.name}</p>}</>)}constPersonPage=()=>{const[userName, setUserName]=useState('')constchangeName=(name)=>{setUserName(name);}return(<><AsyncPage name={userName}/><br/><button onClick={()=>{changeName('张三')}}>张三
            </button><br/><button onClick={()=>{changeName('李四')}}>李四
            </button></>)}

上面代码中,将之前的例子封装成了自己的Hooks,便于共享。其中,usePerson()为自定义Hooks,它接受一个字符串,返回一个数组,数组中包括两个数据的状态,之后在使用usePerson()时,会根据传入的参数不同而返回不同的状态,然后很简便的应用于我们的页面中。


本文转载自: https://blog.csdn.net/m0_46612221/article/details/123384046
版权归原作者 橘猫吃不胖~ 所有, 如有侵权,请联系我们删除。

“React Hooks概述及常用的Hooks介绍”的评论:

还没有评论