长文慎入,一文总结归纳 React Hooks 常用场景

npm i eslint-plugin-react-hooks --save-dev

// 2、eslint 配置

{

“plugins”: [

// …

“react-hooks”

],

“rules”: {

// …

“react-hooks/rules-of-hooks”: “error”,

“react-hooks/exhaustive-deps”: “warn”

}

}

7、一些重点
  • (1)可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate和 componentWillUnmount这三个函数的组合;

  • (2)在 React 的 class 组件中,render 函数是不应该有任何副作用的;一般来说,在这里执行操作太早了,我们基本上都希望在 React 更新 DOM 之后才执行我们的操作。

三、useContext

用来处理多层级传递数据的方式,在以前组件树中,跨层级祖先组件想要给孙子组件传递数据的时候,除了一层层 props 往下透传之外,我们还可以使用 React Context API 来帮我们做这件事。使用例子如下所示 (1)使用 React Context API,在组件外部建立一个 Context

import React from ‘react’;

const ThemeContext = React.createContext(0);

export default ThemeContext;

(2)使用 Context.Provider提供了一个 Context 对象,这个对象可以被子组件共享

import React, { useState } from ‘react’;

import ThemeContext from ‘./ThemeContext’;

import ContextComponent1 from ‘./ContextComponent1’;

function ContextPage () {

const [count, setCount] = useState(1);

return (

<ThemeContext.Provider value={count}>

</ThemeContext.Provider>

<button onClick={() => setCount(count + 1)}>

Click me

);

}

export default ContextPage;

(3)useContext()钩子函数用来引入 Context 对象,并且获取到它的值 // 子组件,在子组件中使用孙组件

import React from ‘react’;

import ContextComponent2 from ‘./ContextComponent2’;

function ContextComponent () {

return (

);

}

export default ContextComponent;

// 孙组件,在孙组件中使用 Context 对象值

import React, { useContext } from ‘react’;

import ThemeContext from ‘./ThemeContext’;

function ContextComponent () {

const value = useContext(ThemeContext);

return (

useContext:{value}

);

}

export default ContextComponent;

四、useReducer

1、基础用法

比 useState 更适用的场景:例如 state 逻辑处理较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等;例子如下所示

import React, { useReducer } from ‘react’;

interface stateType {

count: number

}

interface actionType {

type: string

}

const initialState = { count: 0 };

const reducer = (state:stateType, action:actionType) => {

switch (action.type) {

case ‘increment’:

return { count: state.count + 1 };

case ‘decrement’:

return { count: state.count - 1 };

default:

throw new Error();

}

};

const UseReducer = () => {

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

return (

useReducer Count:{state.count}

<button onClick={() => { dispatch({ type: ‘decrement’ }); }}>useReducer 减少

<button onClick={() => { dispatch({ type: ‘increment’ }); }}>useReducer 增加

);

};

export default UseReducer;

2、惰性初始化 state

interface stateType {

count: number

}

interface actionType {

type: string,

paylod?: number

}

const initCount =0

const init = (initCount:number)=>{

return {count:initCount}

}

const reducer = (state:stateType, action:actionType)=>{

switch(action.type){

case ‘increment’:

return {count: state.count + 1}

case ‘decrement’:

return {count: state.count - 1}

case ‘reset’:

return init(action.paylod || 0)

default:

throw new Error();

}

}

const UseReducer = () => {

const [state, dispatch] = useReducer(reducer,initCount,init)

return (

useReducer Count:{state.count}

<button onClick={()=>{dispatch({type:‘decrement’})}}>useReducer 减少

<button onClick={()=>{dispatch({type:‘increment’})}}>useReducer 增加

<button onClick={()=>{dispatch({type:‘reset’,paylod:10 })}}>useReducer 增加

);

}

export default UseReducer;

五、Memo

如下所示,当父组件重新渲染时,子组件也会重新渲染,即使子组件的 props 和 state 都没有改变

import React, { memo, useState } from ‘react’;

// 子组件

const ChildComp = () => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

// 父组件

const Parent = () => {

const [count, setCount] = useState(0);

return (

hello world {count}

);

};

export default Parent;

改进:我们可以使用 memo 包一层,就能解决上面的问题;但是仅仅解决父组件没有传参给子组件的情况以及父组件传简单类型的参数给子组件的情况(例如 string、number、boolean等);如果有传复杂属性应该使用 useCallback(回调事件)或者 useMemo(复杂属性)

// 子组件

const ChildComp = () => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

const MemoChildComp = memo(ChildComp);

六、useMemo

假设以下场景,父组件在调用子组件时传递 info 对象属性,点击父组件按钮时,发现控制台会打印出子组件被渲染的信息。

import React, { memo, useState } from ‘react’;

// 子组件

const ChildComp = (info:{info:{name: string, age: number}}) => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

const MemoChildComp = memo(ChildComp);

// 父组件

const Parent = () => {

const [count, setCount] = useState(0);

const [name] = useState(‘jack’);

const [age] = useState(11);

const info = { name, age };

return (

hello world {count}

);

};

export default Parent;

分析原因:

点击父组件按钮,触发父组件重新渲染;父组件渲染,const info = { name, age } 一行会重新生成一个新对象,导致传递给子组件的 info 属性值变化,进而导致子组件重新渲染。

解决:

使用 useMemo 将对象属性包一层,useMemo 有两个参数:

  • 第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;

  • 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。

import React, { memo, useMemo, useState } from ‘react’;

// 子组件

const ChildComp = (info:{info:{name: string, age: number}}) => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

const MemoChildComp = memo(ChildComp);

// 父组件

const Parent = () => {

const [count, setCount] = useState(0);

const [name] = useState(‘jack’);

const [age] = useState(11);

// 使用 useMemo 将对象属性包一层

const info = useMemo(() => ({ name, age }), [name, age]);

return (

hello world {count}

);

};

export default Parent;

七 、useCallback

接着第六章节的例子,假设需要将事件传给子组件,如下所示,当点击父组件按钮时,发现控制台会打印出子组件被渲染的信息,说明子组件又被重新渲染了。

import React, { memo, useMemo, useState } from ‘react’;

// 子组件

const ChildComp = (props:any) => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

const MemoChildComp = memo(ChildComp);

// 父组件

const Parent = () => {

const [count, setCount] = useState(0);

const [name] = useState(‘jack’);

const [age] = useState(11);

const info = useMemo(() => ({ name, age }), [name, age]);

const changeName = () => {

console.log(‘输出名称…’);

};

return (

hello world {count}

);

};

export default Parent;

分析下原因:

点击父组件按钮,改变了父组件中 count 变量值(父组件的 state 值),进而导致父组件重新渲染;父组件重新渲染时,会重新创建 changeName 函数,即传给子组件的 changeName 属性发生了变化,导致子组件渲染;

解决:

修改父组件的 changeName 方法,用 useCallback 钩子函数包裹一层, useCallback 参数与 useMemo 类似

import React, { memo, useCallback, useMemo, useState } from ‘react’;

// 子组件

const ChildComp = (props:any) => {

console.log(‘ChildComp…’);

return (

ChildComp…
);

};

const MemoChildComp = memo(ChildComp);

// 父组件

const Parent = () => {

const [count, setCount] = useState(0);

const [name] = useState(‘jack’);

const [age] = useState(11);

const info = useMemo(() => ({ name, age }), [name, age]);

const changeName = useCallback(() => {

console.log(‘输出名称…’);

}, []);

return (

hello world {count}

);

};

export default Parent;

八、useRef

以下分别介绍 useRef 的两个使用场景:

1、指向 dom 元素

如下所示,使用 useRef 创建的变量指向一个 input 元素,并在页面渲染后使 input 聚焦

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

tate(‘jack’);

const [age] = useState(11);

const info = useMemo(() => ({ name, age }), [name, age]);

const changeName = useCallback(() => {

console.log(‘输出名称…’);

}, []);

return (

hello world {count}

);

};

export default Parent;

八、useRef

以下分别介绍 useRef 的两个使用场景:

1、指向 dom 元素

如下所示,使用 useRef 创建的变量指向一个 input 元素,并在页面渲染后使 input 聚焦

最后

文章到这里就结束了,如果觉得对你有帮助可以点个赞哦

开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

[外链图片转存中…(img-iecsHhr1-1714203096765)]

  • 11
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值