6个React Hook最佳实践技巧

2020 年 11 月 16 日

6个React Hook最佳实践技巧


本文最初发布于 Medium 网站,经原作者授权由 InfoQ 中文站翻译并分享。


在过去,像状态和生命周期函数这样的 React 特性只适用于基于类的组件。基于函数的组件被称为哑(dumb)、瘦(skinny)或表示(presentational)组件,因为它们无法访问状态和生命周期函数。


但是自从 React Hooks 发布以来,基于函数的组件已升格为 React 的一等公民。它使函数组件能够以新的方式编写、重用和共享 React 代码。


在这篇文章中,我将分享 6 个关于 React Hooks 的技巧。你可以把它当作一份指南,在将 Hooks 实现到组件中时可以拿来参考。

遵守 Hooks 规则

这条规则看起来是句废话,但无论是新手还是经验丰富的 React 开发人员,都常常会忘记遵循 React Hooks 的规则。这些规则包括:

仅在顶级调用 Hooks

不要在循环、条件和嵌套函数内调用 Hooks。当你想有条件地使用某些 Hooks 时,请在这些 Hooks 中写入条件。


不要这样做:

if (name !== '') { useEffect(function persistForm() {   localStorage.setItem('formData', name); });}
复制代码


相比之下,你应该这样做:


useEffect(function persistForm() {  if (name !== '') {    localStorage.setItem('formData', name);  }});
复制代码


这条规则能确保每次渲染组件时都以相同的顺序调用 Hooks。这样一来,React 就能在多个 useState 和 useEffect 调用之间正确保留 Hooks 的状态。

仅从函数组件调用 Hooks

不要从常规 JavaScript 函数中调用 Hooks。仅从函数组件或自定义 Hooks 中调用 Hooks。


遵循这一条规则,可以确保组件中的所有状态逻辑在源代码中都能清晰可见。

使用 ESLint 的 React Hooks 插件

React 团队还创建了一个名为 eslint-plugin-react-hooks 的 ESLint 插件,以帮助开发人员在自己的项目中以正确的方式编写 React Hooks。这个插件能够帮助你在尝试运行应用程序之前捕获并修复 Hooks 错误。


它有两条简单的规则:


  • react-hooks/rules-of-hooks

  • react-hooks/exhaustive-deps


第一条规则只是强制你的代码符合我在第一个技巧中说明的 Hooks 规则。第二个规则,exhaustive-deps 用于实施 useEffect 的规则:effect 函数中引用的每个值也应出现在依赖项数组中。


例如,下面这个 userInfo 组件会触发 exhaustive-deps 警告,因为 userId 变量在 useEffect 内部被引用,但未在依赖项数组中传递:

function UserInfo({userId}) {  const [user, setUser] = useState(null)  useEffect(() => {    getUser(userId).then(user => setUser(user))  }, []) // no userId here  return <div>User detail:</div>}
复制代码


尽管 exhaustive-deps 这条规则看起来很烦人,但它能帮助你避免由未列出的依赖项引发的错误。

以正确的顺序创建函数组件

当创建类组件时,遵循一定的顺序可以帮助你更好地维护和改进 React 应用程序代码。


首先调用构造器并启动状态。然后编写生命周期函数,接着编写与组件作业相关的所有函数。最后编写 render 方法:

const propTypes = {  id: PropTypes.number.isRequired,  url: PropTypes.string.isRequired,  text: PropTypes.string,};

const defaultProps = { text: 'Hello World',};class Link extends React.Component { static methodsAreOk() { return true; } constructor(props) { super(props) this.state = { user = null } } componentDidMount() { console.log('component did mount') } componentDidUpdate() { console.log('component did update') } componentWillUnmount() { console.log('component will unmount') } render() { return <a href={this.props.url}>{this.props.text}</a> }}Link.propTypes = propTypesLink.defaultProps = defaultPropsexport default Lin
复制代码


编写函数组件时并没有构造器和生命周期函数,因此你可能会犯糊涂,因为这种结构并不像类组件里那样是强制的:

function App() {  const [user, setUser] = useState(null);  useEffect(() => {    console.log("component is mounted");  }, []);  const [name, setName] = useState('');  return <h1>React component order</h1>;}
复制代码


但就像类组件一样,为函数组件创建定义的结构能够改善项目的可读性。


建议先使用 useState Hook 声明状态变量,然后使用 useEffect Hook 编写订阅,接着编写与组件作业相关的其他函数。


最后,你得返回要由浏览器渲染的元素:

function App() {  const [user, setUser] = useState(null);  const [name, setName] = useState('');  useEffect(() => {    console.log("component is mounted");  }, []);  return <h1>React component order</h1>;}
复制代码


通过强制一种结构,可以让代码流在众多组件之间保持一致,看起来也比较亲切。

useState 的用法可以和类组件的状态完全一致,不只用于单个值

许多 useState 示例会向你展示如何通过声明多个变量来声明多个状态:

const [name, setName] = useState('John Doe');const [email, setEmail] = useState('johndoe@email.com');const [age, setAge] = useState(28);
复制代码


但是 useState 实际上既可以处理数组也可以处理对象。你依旧可以将相关数据分组为一个状态变量,如以下示例所示:

const [user, setUser] = useState(  { name: 'John Doe', email: 'john@email.com', age: 28 });
复制代码


这里有一个警告。使用 useState 的更新函数更新状态时,以前的状态会替换为新状态。这与类组件的 this.setState 不同,后者的新类中,新状态会与旧状态合并:

const [user, setUser] = useState(  { name: 'John', email: 'john@email.com', age: 28 });setUser({ name: 'Nathan' });// result { name: 'Nathan' }
复制代码


为了保留以前的状态,你需要创建将当前状态值传递到自身中的回调函数来手动合并它。由于上面的示例已将 user 变量分配为状态值,因此可以将其传递给 setUser 函数,如下所示:

setUser((user) = > ({ ...user, name: 'Nathan' }));// result is { name:'Nathan', email: 'john@email.com', age: 28 }
复制代码


根据数据在应用程序生命周期中的变化情况,建议在各个值彼此独立时将状态拆分为多个变量。


但是对于某些情况,例如构建一个简单的表单,最好将状态分组在一起,以便更轻松地处理更改和提交数据。


简而言之,你需要在多个 useState 调用和单个 useState 调用之间保持平衡。

使用自定义 Hooks 共享应用程序逻辑


在构建应用程序时,你会注意到一些应用程序逻辑会在许多组件中一次又一次地使用。


随着 React Hooks 的发布,你可以将组件的逻辑提取到可重用的函数中作为自定义 Hooks,如我在以下文章中所展示的那样:


可扩展 React 项目的 6 个技巧和最佳实践:

https://blog.bitsrc.io/best-practices-and-tips-for-a-scalable-react-application-db708ae49227


你可以使用 Bit 之类的工具将 Hooks 发布到单个集合中,这样你就可以在不同的应用程序中安装和重用它们。它不需要你创建一个全新的“Hooks 库”项目,你可以一点点将新的 Hooks 从任何项目“推入”你的共享集合。



针对这个方法,唯一要强调的是你不能在类组件中使用 Hooks。所以如果你的项目中还有老式的类组件,就需要将它们转换为函数,或者使用其他可重用逻辑模式(HOC 或渲染 Props)。

使用 useContext 避免 prop drilling

prop-drilling 是 React 应用程序中的常见问题,指的是将数据从一个父组件向下传递,经过各层组,直到到达指定的子组件,而其他嵌套组件实际上并不需要它们。


考虑以下示例:


https://bit.dev/nsebhastian/tutorial-examples/prop-drill-example?example=5f941e4445728c001924150a


从示例中可以看到,即使 Hello 组件不需要 props,App 组件也会通过 Hello 组件将 name props 传递给 Greeting 组件。


React Context 是一项功能,它提供了一种通过组件树向下传递数据的方法,这种方法无需在组件之间手动传 props。父组件中定义的 React Context 的值可由其子级通过 useContext Hook 访问。


在下面的示例中,我将 name 数据(而非 props)传递给 Context Provider,给代码做了重构:


https://bit.dev/nsebhastian/tutorial-examples/prop-drill-example?example=5f941fae45728c001924150e


App 的任何子组件都可以通过 useContext Hook 访问数据。可以从文档中了解有关 useContext Hook 的更多信息:


https://reactjs.org/docs/hooks-reference.html#usecontext

总结

React Hooks 是 React 库的重要补充,因为它允许你用独一无二的方式编写、重用和共享 React 代码。


随着 Hooks 开始改变开发人员编写 React 组件的方式,需要一套新的编写 React Hooks 的最佳实践,以便多个团队之间更轻松地开发和协作。


虽然本文肯定还有遗漏的内容,但我希望以上分享的技巧能多少帮助你在项目中以正确的方式编写 React Hooks。


查看英文原文:


https://blog.bitsrc.io/best-practices-with-react-hooks-69d7e4af69a7


2020 年 11 月 16 日 10:191119

评论 2 条评论

发布
用户头像
满怀期待的点进来,面无表情的出去
2020 年 11 月 18 日 14:01
回复
用户头像
太基础了,没啥实践
2020 年 11 月 16 日 15:07
回复
没有更多评论了
发现更多内容

一致哈希

王鹏飞

第05周 技术选型-01 学习总结

Jaye

数据库周刊31丨openGauss 正式开源;7月数据库排行榜发布;oracle ADG跨版本搭建;PG解决社保问题;mysqlbinlog解析……

墨天轮

MySQL 数据库 oracle 性能优化 opengauss

架构师训练营--第五周学习总结

花花大脸猫

极客大学架构师训练营

架构师训练营 - 第五周 - 学习总结

stardust20

一致性hash算法的实现和平衡性测试

周冬辉

深入了解kafka系列-消费者

小技术君

kafka flink 消费 kafka Kafka知识点

kafka监听mysql实时数据变更

爱java爱自己

MySQL mysql事务

朱嘉明教授获2020杭州区块链国际周“特别致敬奖”

CECBC区块链专委会

CECBC 朱嘉明 区块链国际周 特别致敬

作业

chenzt

新增的两个区块链职业到底是做什么的?

CECBC区块链专委会

技术人才 系统操作 需求落地 框架搭建

Spring Boot读取配置文件的几种方式

Java旅途

Spring Boot properties yaml

第五周总结

考尔菲德

架构师训练营第五周总结

方堃

极客大学架构师训练营

05周作业—技术选型

dao

极客大学架构师训练营 作业 一致性Hash算法

架构师训练营第五周作业

架构师 极客大学架构师训练营

架构师训练营 -- 第五周作业

stardust20

分布式缓存一致性hash算法实现

考尔菲德

Week05 作业

极客大学架构师训练营

分布式缓存总结

罗亮

“区块链+” 医疗行业场景应用迎来大发展

CECBC区块链专委会

医疗方案 区块链+ 场景应用落地 多元场景应用

1. 初识Jackson -- 世界上最好的JSON库

YourBatman

Jackson Fastjson JSON库

架构师训练营 - 学习笔记 - 第五周

心在飞

极客大学架构师训练营

啃碎并发(四):Java线程Dump分析

猿灯塔

深入了解kafka系列-生产者

小技术君

kafka Kafka知识点

架构师训练营 第五周 总结

Poplar

一致性Hash算法

南宫煌

极客大学架构师训练营

哈希表

Arthur

Scrum Master与Project Manager的区别

Mew

Scrum

架构师训练营——第5周学习总结

jiangnanage

架构设计篇之面向对象设计

小诚信驿站

架构 架构师 架构分析 刘晓成 架构演进

AI如何在普惠金融的探索中发挥作用?

AI如何在普惠金融的探索中发挥作用?

6个React Hook最佳实践技巧-InfoQ