当前位置: 首页 > news >正文

React 解释常见的 hooks: useState / useRef / useContext / useReducer

前言

如果对 re-render 概念还不清楚,建议先看 React & 理解 re-render 的作用、概念,并提供详细的例子解释 再回头看本文。

如果对 React 基础语法还不熟练,建议先看 React & JSX 日常用法与基本原则 再回头看本文。

useState

useState 可以用来声明响应式数据。

使用案例:

import ReactDOM from 'react-dom/client';
import { useState } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {const [name, setName] = useState('Jack')return <><div>My name is {name}</div><button onClick={() => setName('John')}>Change my name</button></>
}
root.render(<App />
);

效果:

请添加图片描述

注意,state 更新采用异步执行 re-render,因此控制台打印的值仍然是旧的。
在这里插入图片描述

若希望能够在 state 变更后马上拿到最新的值,有两种方案:

  1. 使用临时变量存储要变更的值:
// ...
const onChangeName = () => {const tempName = 'John'setName(tempName)console.log('My current name is', tempName)}
// ...
  1. 使用 useRef,下面会讲。

useRef

useRef 也是声明响应式数据的一种方式,与 useState 不同的是,它可以不受 re-render 的约束,在更新数据后能立刻访问最新的值。

使用案例:

import ReactDOM from 'react-dom/client';
import { useRef } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {const name = useRef('Jack')const onChangeName = () => {name.current = 'John'console.log('My current name is', name.current)}return <><div>My name is {name.current}</div><button onClick={onChangeName}>Change my name</button></>
}
root.render(<App />
);

效果:
请添加图片描述
可以看出 useRef useState 是两种相反的结果,控制台为最新,视图为旧。
在不强调视图的情况可以考虑用 useRef ,比如异步回调获取最新数据的场景。

useReducer

useReduceruseState 的进阶版,当 useState 操作逻辑变得复杂时,可以将它们提升为 useReducer 的书写方式 ,提高可读、维护性。

下面是 useEffect VS useReducer 相同功能的对比图:
在这里插入图片描述
useReducer 通过 switch 声明式结构,我们能一眼就能看出每个 type 都做了哪些事。
从对比图看,虽然没能展现 useReducer 的优势,但我们只要理解它的逻辑处理结构就行了,
多一种选择,何乐而不为呢。

在 useState 处理少量逻辑的情况下优先 useState,反之 useReducer

案例代码:

import ReactDOM from 'react-dom/client';
import { useReducer } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));const todoReducer = (state, action) => {switch (action.type) {case 'add':return {...state,todos: [...state.todos,Math.floor(Math.random() * 1000)]}case 'remove':return {...state,todos: state.todos.splice(state.index)}default:return state}
}
const App = () => {const [state, dispatch] = useReducer(todoReducer, { todos: [] })return <><header>Todos</header><button onClick={() => dispatch({ type: 'add' })}>Add todo</button><button onClick={() => dispatch({ type: 'remove' })}>Remove todo</button><ul>{state.todos.map((todo) => (<li key={todo}>{todo}</li>))}</ul></>
}
root.render(<App />
);

参数解释:useReducer(reducer, initialArg, init?)

  • 第一个参数是一个 handler,即要处理数据的函数。
  • 第二个参数是默认值,和 useState(xx) 的第一个参数一样。
  • 第三个参数是一个 useMemo 回调函数(可选项),它可以缓存第二个参数的数据,避免 re-render 重复定义默认值。

useReducer 返回的 state / dispatch 表示:

  • state 获取我们的数据。
  • dispatch 触发 handler 函数

提示:掌握了 useReducer 等于学会了 react-redux 框架,它与 useReducer 的区别仅仅多了一层全局缓存的含义,对 react-redux 感兴趣的可参考:
React & 用一个简单案例体验一遍 React-dom & React-router & React-redux 全家桶

在这里插入图片描述

useContext

useContext 可以解决多层组件传递 props 数据的问题,如果你用过 Vue 的 provide/inject 函数,那你也就会了,它们俩的作用&概念是一致的。

案例代码:

import ReactDOM from 'react-dom/client';
import { createContext, useContext, useState } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const Profile = () => {const userInfo = useContext(UserInfoContext)return <><label>Name: {userInfo.name}</label><br /><label>Age: {userInfo.age}</label><br /><label>Hobbies: {userInfo.hobbies.map(hobby => <span key={hobby}>{hobby} </span>)}</label></>
}
const ShoppingCart = () => {const userInfo = useContext(UserInfoContext)return <><footer>Shopping Cart:</footer><ul>{userInfo.carts.map((cart) => <li key={cart}>{cart}</li>)}</ul>Total num: {userInfo.carts.length }</>
}const UserInfoContext = createContext(null)
const App = () => {const [userInfo] = useState({name: 'Jack',age: 30,hobbies: ['Running', 'Painting'],carts: ['Dog toy', 'Cup']})return <><UserInfoContext.Provider value={userInfo}><Profile/><ShoppingCart/></UserInfoContext.Provider></>
}
root.render(<App />
);

效果:
在这里插入图片描述

useMemo / useCallback

useMemo 可以缓存非响应式数据,避免 re-render 的问题。

useCallback 可以缓存函数,避免 re-render 的问题。

对于函数缓存,虽然 useMemo 也能实现,但还是得额外嵌套一层函数,因此官方建议使用 useCallback

俩钩子的用法在 React & 理解 re-render 的作用、概念,并提供详细的例子解释 都有详细的解释,这里不再赘述。


完!

http://www.lryc.cn/news/450673.html

相关文章:

  • telnet发送邮件教程:安全配置与操作指南?
  • 超强大的 Nginx 可视化管理工具
  • Android 安装应用-提交阶段之后剩下的操作
  • buuctf [ACTF2020 新生赛]Include
  • JS使用MutationObserver接口来监听DOM的更新
  • 图解C#高级教程(三):泛型
  • 240930_CycleGAN循环生成对抗网络
  • ide 使用技巧与插件推荐
  • 【node】 cnpm|npm查看、修改镜像地址操作 换源操作
  • 大数据-152 Apache Druid 集群模式 配置启动【下篇】 超详细!
  • IDE 使用技巧与插件推荐全面指南
  • java-快速将普通main类变为javafx类,并加载自定义fxml
  • 数据结构之——单循环链表和双向循环链表
  • Git Stash: 管理临时更改的利器
  • ELK--收集日志demo
  • Redis的主要特点及运用场景
  • 与我免费ai书童拆解《坚持》创作历程
  • 昇思MindSpore进阶教程--下沉模式
  • Hive SQL业务场景:连续5天涨幅超过5%股票
  • Java 如何从图片上提取文字
  • C#进阶-读写Excel常用框架及其使用方式
  • Python爬虫lxml模块安装导入和xpath基本语法
  • python魔法(python高级magic方法进阶)
  • 【论文笔记】Flamingo: a Visual Language Model for Few-Shot Learning
  • 问:JAVA阻塞队列实现类及最佳实践?
  • Springboot3 + MyBatis-Plus + MySql + Vue + ProTable + TS 实现后台管理商品分类(最新教程附源码)
  • 消费电子制造企业如何使用SAP系统提升运营效率与竞争力
  • 算法记录——树
  • 单片机在控制和自动化任务中的应用场景广泛
  • UEFI EDK2框架学习(三)——protocol