(编辑:jimmy 日期: 2024/12/23 浏览:2)
最近由于公司的项目开发,就学习了在react关于hook的使用,对其有个基本的认识以及如何在项目中去应用hook。在这篇博客中主要从以下的几个点进行介绍:
首先介绍关于hook的含义,以及其所要去面对的一些场景
含义:Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。简单来说就是可以使用函数组件去使用react中的一些特性
所要解决的问题:
那么接下来所要介绍的部分就是如何去使用hook
对于使用过class组件的同学,相信对于state肯定有很深的印象,对于一些需要用到的全局变量,在class组件中我们常常采用的方式是this.state = {},但是在hook中我们采用的方式就是使用useState这个hook,然后就可以对这种全局变量进行引用,在引用时只需要用其变量名即可,这里就拿官网的例子来举例:
import React, { useState } from 'react';
import React, { useState } from 'react'; function Example() { // 声明一个叫 "count" 的 state 变量 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
在上面的这个例子中,我们设置变量方式采用的就是const [count, setCount] = useState(0)这种方式,其中的0就是给count赋初值为0,如果想要给count赋值为一个空对象,那么只需要const [count, setCount] = useState({}),这样的方式就行了,那么这样你在用count时,此时获取到的值就为一个空对象。
作用:返回一个state,以及更新state的函数
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; })
在class组件中我们给放在state中的变量赋值时,通常采用的方式就是this.setState()这种方式,那么在hook中所要采用的就是set+变量名这种方式,如
const [count, setCount] = useState(0)
在这里通过上面我们已经知道的就是count能够获取到值,那么其所对应的setCount(值),这种赋值的方式就是给count变量赋值的,然后通过count就能够获取到值。
在hook中,如果我们需要去设置多个类似于上面所说的count,那么就需要多次使用useState这个hook,当然你也可以设置一个变量在hook的最外部,即在hook这个函数组件的外部。需要注意的是别在整个hook这个函数的全局设置,因此在hook的运行机制中,在每次加载时,都会从新去加载里面的变量,因此你是不能够去获取到在整个函数内部中使用该变量所改变的值的,能够获取到的就只是这个变量的初始值*
对于useEffect hook,其用途类似于class组件中的生命周期函数,用来处理在一些特定时刻需要去做的事情,这种事情常被叫做副作用。在使用useEffect这个hook时,需要注意的一点就是其不能够被包含在循环,判断语句中,否则项目会出现报错,这也是hook的一种设置机制
对于useEffect的初步认识只需要了解上面的即可。接下来就来介绍一个官网的实例,来说明useEffect
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
在上面的这段代码中,就使用到了useEffect这个hook,在每次count值改变时,就会在页面中去打印“You clicked ${count} times”这段文字,当然count肯定对应的就是其所对应的值。
react中有状态组件中,其生命周期函数的各个阶段
这里就介绍了关于useEffect这个hook的使用,有一些生命周期函数就是通过该hook来实现的,这里推荐一篇文章https://blog.logrocket.com/guide-to-react-useeffect-hook/,可以参考下。这里是在参考了一些文章后写的,具体介绍如下:
constructor: 可以通过useState来初始化state
componentDidMount(),在hook中需要使用下面的这种方式去取代,在useEffect中传递第二个参数,该参数为一个空数组,只会去执行一次,如下面所示
useEffect(() => { },[])
componentDidUpdate(),有两种方式去解决
在每次渲染的时候都去调用hooks,解决的方式如下面所示
useEffect(() => { })
用一个特殊变量的去触发hook,如下面所示,count指的就是这个特殊的变量,该hook触发,只会是count的值改变时
useEffect(() => { },[count])
componentWillUnmount(),用hook来代替,需要去return一个callback(回调函数),如下面的形式所示
useEffect(() => { return () => { //执行的为componentWillUnmount } },[])
shouldComponentUpdata(),常使用React.memo来代替,在默认情况下,它将对props对象中的复杂对象进行浅层比较,如果想要去控制比较,可以去提供一个自定义的比较函数作为第二个参数。代替hook的方式如下所示
import React from 'react' function areEqual(prevProps, nextProps) { /* return true if passing nextProps to render would return the same result as passing prevProps to render, otherwise return false */ } const Weather = ({weather}) => { return (<div> <p>{weather.city}</p> <p>{weather.temperature}</p> {console.log('Render')} </div> ) } export default React.memo(Weather, areEqual)
通常在实际的项目开发中少不了使这种自定义的hook,前提是在整个项目中使用了hook的情况下。通常情况下就是去使用useState,useEffect这种系统已经定义好的hook去实现,在调用时你就可以直接调用当你自定义好的hook来实现你所需要的功能。下面就以自定义useReducer这个hook为例
import React, { useEffect } from 'react' function useReducer(reducer, initialState) { const [state, setState] = useState(initialState); function dispatch(action) { const nextState = reducer(state, action); setState(nextState); } return [state, dispatch]; } export default useReducer
在上面的这个实际例子中,我们封装了一个自定义的useReducerhook,我们可以调用这个hook去完成与reducer一样的功能了,在调用是就需要我们去传入两个参数,一个就是reducer,另外一个就是initialState,然后就能够取得state,以及dispatch方法。注意这里的返回值使用的是一个数组,这样的好处就是我们在获取其返回值时,可以采用数组结构这种方式来获取。具体关于数组的结构可以去看看es6中的部分,就能够明白了。那么接下来就是使用这个自定义好的useReducer。使用方式如下
import useReducer form '你封装useRecuer的组件中' function Todos() { const todosReducer = ( state, dispatch) => { if(dispatch.type == "") { //type值为什么时去执行 const newState == "" //执行一些操作,去更新state return newState //返回新的neState } } const [todos, dispatch] = useReducer(todosReducer, []); function handleAddClick(text) { dispatch({ type: 'add', text }); } return ( <div></div> ) }
这里并没有把实际的使用情况给写完,剩余的可以自己去补充,其使用方式就和redux的使用方式相同。这就是整个自定义hook以及去使用的过程,在实际的开发中可以去体验体验。
useReducer,能给那些会出发深更新的组件做性能优化,因为可以向子组件去传递dispatch而不是回调
useReducer这个hook的封装,整个封装的方法如下:
//reducer hook封装 import { useState } from 'react'; export default useReducer function(reducer, initialState) { const [state, setState] = useState(initialState); function dispatch(action){ const nextState = reducer(state, action); return setState(nextState); } return [state, dispatch] } //实际例子使用 import useReducer from ''; const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } return ( <div> Count: {state.count} <button onClick={() => dispatch({type: 'devrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </div> )
useReducer的惰性初始化,可以选择惰性地创建初始化state。因此需要设置一个初始化函数作为useReducer的第三个参数传入,这样初始化state将设置为init(initialArg),如下所示,就是一个实际的案例在useReducer中去传递第三个参数
function init(initialCount) { return {count: initialCount}; } function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; case 'reset': return init(action.payload); default: throw new Error(); } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialCount, init); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
注意:如果reducer hook的返回值与当前state相同,react将跳过子组件的渲染及副作用的执行
useCallback
返回值:返回一个memoized回调函数,该回调函数仅在某给依赖项改变时才会更新。
含义:把内联回调函数及其依赖项数组作为参数传入useCallback,它将返回该回调函数传递给经过优化的并使用引用相等性去避免非必要渲染
useCallBack(fn, deps)相当与useMemo(() => fn,deps)
useMemo
使用方式:const memoziedValue = useMemo(() => computeExpensiveValue(a,b), [a, b])
返回值:返回一个memoized值,把创建函数和依赖项数组作为参数传入useMemo,仅在某个依赖项改变时才重新计算memoized值。
好处:这种优化有助于避免在每次渲染时都进行高开销的计算
渲染方式:传入useMemo的函数会在渲染期间执行,不要在这个函数内部执行与渲染无关的操作,如属于useEffect中的副作用。如果没有,那么新的值将会在每次渲染时被重新渲染
注意:依赖项数组不会作为参数传递给函数,概述来说,就是每一个出现在函数中的参数也应该出现在依赖项的数组中
useRef
使用方式: const refContainer = useref(initialValue);
返回值:返回一个可ref对象,其.current属性被初始化为传入的参数(initialValue)。这返回的的对象将在组件的整个生命周期中持续
含义: useRef就像是一个盒子可以将.current中得可变属性给保存起来
ref与useRef的区别在于,后者是创建的了一个普通的js对象,useRef和自建一个{current: …。}对象的唯一区别是,useRef会在每次渲染时,返回同一个ref对象
useImperativeHandle
作用:可以在使用ref时自定义暴露给赋组件的实例值,使用的形式如下:
useImperativeHandle(ref, createHandle, [deps])
useLayoutEffect
更新时机:在浏览器执行下一次绘制前去执行
与useEffect相同,会在所有的DOM变更之后同步调用effect
useDebugValue
作用:在react devTools中常被用于去当作展示标签,作为客户端的钩子
在hook中,其性能优化的点很多,这个可以在一些https://react.docschina.org/docs/hooks-faq.html#performance-optimizations去学习,下面是我看的一部分。
如何在更新时去跳过effect,可以采用条件式方式,即在useEffect中去传递第二个参数
由于某些原因,无法将一个函数移动到effect内部时,可采用下面方式
function ProductPage({ productId }) { // Wrap with useCallback to avoid change on every render const fetchProduct = useCallback(() => { // ... Does something with productId ... }, [productId]); // All useCallback dependencies are specified return <ProductDetails fetchProduct={fetchProduct} />; } function ProductDetails({ fetchProduct }) { useEffect(() => { fetchProduct(); }, [fetchProduct]); // All useEffect dependencies are specified // ... }
实现shouldComponentUpdate的方式
const Button = React.memo((props) => { // your component });
如上面所示,这种实现方式并不是使用了hooks,它相当于纯组件,但是仅仅能够比较的是props。可以去增加第二个参数,采用一种函数的方式去拿到新老的props,如果结果返回true,就跳过更新阶段
记住计算结果的方式
使用useMemo这个hook去记住之前的计算结果,从而在多个渲染之中缓存计算
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
上面的代码会调用computeExpensiveValue(a,b)这个函数,但是它们依赖的a,b没有改变,那么useMemo在直接去返回上一次结果的值
对于hook的学习大概就如上面所说,对于hook其中的内容还很多所以对于hook的学习最好是去官网看看,链接如下https://react.docschina.org/docs/hooks-intro.html在官网中介绍的更加详细,这里的中文文档和英文文档内容都一样,不过对于英文好的同学建议看看英文版本。