一文归纳 React Hooks 常用场景,移动端前端前端开发

本文详细介绍了React16.8版本后推出的Hooks功能,包括StateHook(useState)、EffectHook、useContext和useReducer,强调了它们如何提高代码可读性、减少组件层级、优化性能以及处理状态管理。作者还提供了实际示例和最佳实践,助读者理解和应用这些新特性。
摘要由CSDN通过智能技术生成

关注公众号 前端开发博客,回复“加群”

加入我们一起学习,天天进步

前言

文章虽然比较长,但是可以说是全网最全最有用的总结了,学会的记得分享、点赞、收藏、谢谢支持

React 在 v16.8 的版本中推出了 React Hooks 新特性。在我看来,使用 React Hooks 相比于从前的类组件有以下几点好处:

  1. 代码可读性更强,原本同一块功能的代码逻辑被拆分在了不同的生命周期函数中,容易使开发者不利于维护和迭代,通过 React Hooks 可以将功能代码聚合,方便阅读维护;

  2. 组件树层级变浅,在原本的代码中,我们经常使用 HOC/render props 等方式来复用组件的状态,增强功能等,无疑增加了组件树层数及渲染,而在 React Hooks 中,这些功能都可以通过强大的自定义的 Hooks 来实现;

关于这方面的文章,我们根据使用场景分别进行举例说明,帮助你认识理解并可以熟练运用 React Hooks 大部分特性。辛苦整理良久,还望手动点赞鼓励~

一、State Hook

1、基础用法

function State(){

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

return (

You clicked {count} times

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

Click me

)

}

2、更新

更新分为以下两种方式,即直接更新和函数式更新,其应用场景的区分点在于:

直接更新不依赖于旧 state 的值;函数式更新依赖于旧 state 的值;

// 直接更新

setState(newCount);

// 函数式更新

setState(prevCount => prevCount - 1);

3、实现合并

与 class 组件中的 setState 方法不同,useState 不会自动合并更新对象,而是直接替换它。我们可以用函数式的 setState 结合展开运算符来达到合并更新对象的效果。

setState(prevState => {

// 也可以使用 Object.assign

return {…prevState, …updatedValues};

});

4、惰性初始化 state

initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略。其应用场景在于:创建初始 state 很昂贵时,例如需要通过复杂计算获得;那么则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用:

const [state, setState] = useState(() => {

const initialState = someExpensiveComputation(props);

return initialState;

});

5、一些重点
  • (1)不像 class 中的 this.setState ,Hook 更新 state 变量总是替换它而不是合并它;

  • (2)推荐使用多个 state 变量,而不是单个 state 变量,因为 state 的替换逻辑而不是合并逻辑,并且利于后续的相关 state 逻辑抽离;

  • (3)调用 State Hook 的更新函数并传入当前的 state 时,React 将跳过子组件的渲染及 effect 的执行。(React 使用 Object.is 比较算法 来比较 state。)

二、Effect Hook

1、基础用法

function Effect(){

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

useEffect(() => {

console.log(You clicked ${count} times);

});

return (

You clicked {count} times

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

Click me

)

}

2、清除操作

为防止内存泄漏,清除函数会在组件卸载前执行;如果组件多次渲染(通常如此),则在执行下一个 effect 之前,上一个 effect 就已被清除,即先执行上一个 effect 中 return 的函数,然后再执行本 effect 中非 return 的函数。

useEffect(() => {

const subscription = props.source.subscribe();

return () => {

// 清除订阅

subscription.unsubscribe();

};

});

3、执行时期

与 componentDidMount 或 componentDidUpdate 不同,使用 useEffect 调度的 effect 不会阻塞浏览器更新屏幕,这让你的应用看起来响应更快;(componentDidMount 或 componentDidUpdate 会阻塞浏览器更新屏幕)

4、性能优化

默认情况下,React 会每次等待浏览器完成画面渲染之后延迟调用 effect;但是如果某些特定值在两次重渲染之间没有发生变化,你可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect 的第二个可选参数即可:如下所示,如果 count 值两次渲染之间没有发生变化,那么第二次渲染后就会跳过 effect 的调用;

useEffect(() => {

document.title = You clicked ${count} times;

}, [count]); // 仅在 count 更改时更新

5、模拟 componentDidMount

如果想只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([ ])作为第二个参数,如下所示,原理跟第 4 点性能优化讲述的一样;

useEffect(() => {

}, []);

6、最佳实践

要记住 effect 外部的函数使用了哪些 props 和 state 很难,这也是为什么 通常你会想要在 effect 内部 去声明它所需要的函数。

// bad,不推荐

function Example({ someProp }) {

function doSomething() {

console.log(someProp);

}

useEffect(() => {

doSomething();

}, []); // ??? 这样不安全(它调用的 doSomething 函数使用了 someProp

}

// good,推荐

function Example({ someProp }) {

useEffect(() => {

function doSomething() {

console.log(someProp);

}

doSomething();

}, [someProp]); // ✅ 安全(我们的 effect 仅用到了 someProp

}

如果处于某些原因你无法把一个函数移动到 effect 内部,还有一些其他办法:

你可以尝试把那个函数移动到你的组件之外。那样一来,这个函数就肯定不会依赖任何 props 或 state,并且也不用出现在依赖列表中了;万不得已的情况下,你可以 把函数加入 effect 的依赖但 把它的定义包裹 进 useCallback Hook。这就确保了它不随渲染而改变,除非它自身的依赖发生了改变;

推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps 规则,此规则会在添加错误依赖时发出警告并给出修复建议 ;

// 1、安装插件

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)=>{

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
img

nt: number

}

interface actionType {

type: string,

paylod?: number

}

const initCount =0

const init = (initCount:number)=>{

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-YX57i3Ro-1710911620199)]
[外链图片转存中…(img-NbRDX38E-1710911620200)]
[外链图片转存中…(img-DwCRCB4V-1710911620201)]
[外链图片转存中…(img-RIg0kQxM-1710911620202)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
[外链图片转存中…(img-kHu9jTGj-1710911620202)]

  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值