首页
关于
翻译
留言
搜索
1
第 4 章 创建您的第一个React组件
493 阅读
2
如何读懂编译后的JS代码
431 阅读
3
使用Jest测试React应用
410 阅读
4
让您的React组件具有响应式
347 阅读
5
第 2 章 为您的项目安装强大的工具
336 阅读
JavaScript
TypeScript
移动
Web
后端
测试
运维
登录
Search
标签搜索
React
Angular
库
组件
工程化
Vue
框架
Node.js
Hook
可视化
性能
兼容
Debug
HTML5
ActionScript
CSS
部署
分享
算法
PHP
Flying
累计撰写
174
篇文章
累计收到
2
条评论
首页
栏目
JavaScript
TypeScript
移动
Web
后端
测试
运维
页面
关于
翻译
留言
搜索到
33
篇
标签为 React 的文章
2022-08-26
React CRUD项目
本项目使用React + TypeScript + antd + axios + json-server开发的简单CRUD Web应用。麻雀虽小,五脏俱全。感兴趣的话,可以访问该项目的Github查看。
2022年08月26日
92 阅读
0 评论
1 点赞
2021-07-02
怎样不输出React工程的sourceMap文件
使用Create React App创建的单页应用生产环境下默认会使用sourceMap文件,这些文件一般很大,最好不要将它们部署到服务器上。当然,我们可以手动删除已经输出的文件,高级一点还可以使用一个node程序来批量删除。但能不能简单一点,通过webpack不输出sourceMap文件呢?Create React App这个工具链是官方推荐的的最佳方式,不过默认下它隐藏了webpack,无法直接修改配置,因此我们想自定义一些配置比较麻烦。目前能自定义的方式主要有:一、使用eject在package.json中很容易找到该命令,运行该命令会还原react-scripts隐藏的webpack配置,通过webpack配置文件我们就可以随心所欲地自定义了。注意,该过程是不可以逆的,会生成很多工程文件。小心使用为妙!二、使用config-overrides.js这是一种通过覆盖webpack原有默认配置的方式一定来自定义的方式。1. 安装依赖npm i -D react-app-rewired customize-cra2. 修改 package.json脚本"scripts": { "start": "react-app-rewired start", "build": "react-app-rewired build", "test": "react-scripts test --env=jsdom" ... }3. 设置config-overrides.js比如我们要在生产环境不输出sourceMap文件,可以在项目的根目录下新建config-overrides.js,然后这样定义:const { override } = require('customize-cra'); module.exports = override( (() => (config, env) => { if (env === 'production') { config.output.publicPath = ''; config.devtool = false; } return config; })() )运行npm run build后会发现,确实不会输出js的sourceMap文件,但css的sourceMap文件依然照输不误。尝试使用customize-cra插件的其它方法也无功而返。直到我仔细阅读了Create React App文档,最终找到了办法。三、使用.env项目的根目录下新建.env.production,该文件只对生产环境有效,然后这样定义:GENERATE_SOURCEMAP = false运行npm run build后,js/css的sourceMap文件都不会输出。一切都安静了,真好!参考:https://create-react-app.dev/docs/advanced-configuration
2021年07月02日
114 阅读
0 评论
1 点赞
2021-05-06
第 11 章 从 React 类组件迁移
在上一章中,我们学习了如何通过从现有代码中提取自定义 Hooks 来构建我们自己的 Hooks。然后,我们在博客应用中使用了我们自己的 Hooks,并了解了本地 Hooks 和 Hooks 之间的交互。最后,我们学习了如何使用 React 的 Hooks 测试库为 Hooks 编写测试,并为我们的自定义 Hooks 实现了测试。在本章中,我们将首先使用 React 类组件实现一个 ToDo 应用。在下一步中,我们将学习如何将现有的 React 类组件应用迁移到 Hooks。在实践中看到使用 Hooks 的函数组件和类组件之间的差异将加深我们对使用任一解决方案的权衡的理解。此外,在本章结束时,我们将能够将现有的 React 应用迁移到 Hooks。本章将介绍以下主题:使用类组件处理状态将应用从类组件迁移到 Hooks了解类组件与 Hooks 的权衡技术要求应该已经安装了相当新版本的 Node.js(vll.l2.0 或更高版本)。Node.js 的 npm 包管理器也需要安装。本章的代码可以在 GitHub 存储库中找到:https://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter11.观看以下视频,了解代码的实际应用:http://bit.ly/2Mm9yoC请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。为了正确学习和理解,自己编写代码很重要。但是,如果遇到任何问题,始终可以参考代码示例。使用类组件处理状态在我们开始从类组件迁移到 Hooks 之前,我们将使用 React 类组件创建一个小型的 ToDo 列表应用。在下一节中,我们将使用 Hooks 将这些类组件转换为函数组件。最后,我们将比较这两种解决方案。设计应用结构正如我们之前对博客应用所做的那样,我们将从考虑应用的基本结构开始。对于此应用,我们将需要以下功能:页眉添加新待办事项的方法一种在列表中显示所有待办事项的方法待办事项的筛选器从模型开始总是一个好主意。那么,让我们开始吧:我们首先为我们的 ToDo 应用绘制一个界面模型:我们的待办事项应用的模型接下来,我们定义基本组件,其方式类似于我们使用博客应用的方式:TodoList 组件使用用于显示项目的 Todoitem 组件,其中包含要完成的复选框和用于删除它的按钮。TodoFilter 组件在内部使用 TodoFilteritem 组件来显示各种过滤器。初始化项目我们将使用 create-react-app 来创建一个新项目。现在让我们初始化项目:运行以下命令:npx create-react-app chapter11_1然后,删除 src/App.css,因为我们不需要它。接下来,编辑 src/index.css,并按如下方式调整边距:margin: 20px;最后,删除当前的 src/App.js 文件,因为我们将在下一步中创建一个新文件。现在,我们的项目已经初始化,我们可以开始定义应用结构了。定义应用结构我们已经从模型中知道应用的基本结构会是什么样子,所以让我们从定义 App 组件开始:创建一个新的 src/App.js 文件。导入 React 和 Header、AddTodo、TodoList 和 TodoFilter 组件:import React from 'react' import Header from './Header' import AddTodo from './AddTodo' import TodoList from './TodoList' import TodoFilter from './TodoFilter'现在将 App 组件定义为类组件。现在,我们只定义 render 方法:export default class App extends React.Component { render() { return ( <div style={{ width: 400 }}> <Header /> <AddTodo /> <hr /> <TodoList /> <hr /> <TodoFilter /> </div> ) } }App 组件定义了我们应用的基本结构。它将包含一个标题、一种添加新待办事项的方法、一个待办事项列表和一个过滤器。定义组件现在,我们将组件定义为静态组件。在本章的后面,我们将为它们实现动态功能。现在,我们将实现以下静态组件:HeaderAddTodoTodoListTodoitemTodoFilter现在让我们开始实现组件。定义标头组件我们将从 Header 组件开始,因为它是所有组件中最简单的:创建一个新的 src/Header.js 文件。导入 React 并使用 render 方法定义类组件:import React from 'react' export default class Header extends React.Component { render() { return <h1>ToDo</h1> } }现在,我们应用的 Header 组件已定义。定义 AddTodo 组件接下来,我们将定义 AddTodo 组件,它呈现一个 input 字段和一个按钮。现在让我们实现 AddTodo 组件:创建一个新的 src/AddTodo.js 文件。导入 React 并定义类组件和 render 方法:import React from 'react' export default class AddTodo extends React.Component { render () { return (在 render 方法中,我们返回一个包含 input 字段和一个添加按钮的 form: <form> <input type="text" placeholder="enter new task..." style={{ width: 350, height: 15 }} /> <input type="submit" style={{ float: 'right', marginTop: 2 }} value="add" /> </form> ) } }如我们所见,AddTodo 组件由一个 input 字段和一个按钮组成。定义待办事项列表组件现在,我们定义 TodoList 组件,它将使用 Todoitem 组件。现在,我们将在此组件中静态定义两个待办事项。让我们开始定义 TodoList 组件:创建一个新的 src/TodoList.js 文件。导入 React 和 Todoitem 组件:import React from 'react' import Todoitem from './Todoitem'然后,定义类组件和 render 方法:export default class TodoList extends React.Component { render () {在这个 render 方法中,我们静态定义两个待办事项:const items = [ { id: 1, title: 'Write React Hooks book', completed: true }, { id: 2, title: 'Promote book', completed: false }, ]最后,我们将使用 map 函数渲染项目:return items.map(item => <Todoitem {...item} key={item.id} /> ) } }如我们所见,TodoList 组件呈现了 Todoitem 组件的列表。定义待办事项组件定义 TodoList 组件后,我们现在将定义Todoitem 组件,以便呈现单个项目。让我们开始定义 Todoitem 组件:创建一个新的 src/Todoitem.js 组件。导入 React,并定义组件,以及 render 方法:import React from 'react' export default class Todoitem extends React.Component { render () {现在,我们将使用解构来获得 title 和 completed props:const { title, completed } = this.props最后,我们将渲染一个包含checkbox、title 和 button 的 div 元素来删除该项目: return ( <div style={{ width: 400, height: 25 }}> <input type="checkbox" checked={completed} /> {title} <button style={{ float: 'right' }}>x</button> </div> ) } }Todoitem 组件由一个复选框、一个 title 和一个用于删除项目的 button 组成。定义 TodoFilter 组件最后,我们将定义 TodoFilter 组件。在同一文件中,我们将为 TodoFilteritem 定义另一个组件。让我们开始定义 TodoFilteritem 和 TodoFilter 组件:创建一个新的 src/TodoFilter.js 文件。为 TodoFilteritem 定义一个类组件:class TodoFilteritem extends React.Component { render () {在这个渲染方法中,我们使用解构来获取名称 prop:const { name } = this.props接下来,我们将为 style 定义一个对象:const style = { color: 'blue', cursor: 'pointer', }然后,我们返回一个带有过滤器 name 值的 span 元素,并使用定义的 style 对象:return <span style={style}>{name}</span> } }最后,我们可以定义实际的 TodoFilter 组件,它将呈现三个 TodoFilteritem 组件,如下所示:export default class TodoFilter extends React.Component { render() { return ( <div> <TodoFilteritem name="all" /> {' / '} <TodoFilteritem name="active" /> {' / '} <TodoFilteritem name="completed" /> </div> ) } }现在,我们有一个组件列出了三种不同的过滤器可能性:all、active 和 completed。实现动态代码现在我们已经定义了所有静态组件,我们的应用应该看起来就像模型一样。下一步是使用 React 状态、生命周期和处理程序方法实现动态代码。在本节中,我们将执行以下操作:定义模拟 API定义 StateContext使 App 组件动态化使 AddTodo 组件动态化使 TodoList 组件动态化使 Todoitem 组件动态化使 TodoFilter 组件动态化让我们开始吧。定义 API 代码首先,我们将定义一个将获取待办事项的 API。在我们的例子中,我们只是在短暂的延迟后返回一系列待办事项。让我们开始实现模拟 API:创建一个新的 src/api.js 文件。我们将定义一个函数,该函数将根据 Universally Unique Identifier(UUID)——通用唯一标识符函数为我们的待办事项生成随机 ID:export const generateID = () => { const S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1) return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4() }然后,我们定义 fetchAPiTodos 函数,它返回一个 Promise,该函数在短暂延迟后解析:export const fetchAPITodos = () => new Promise((resolve) => setTimeout( () => resolve([ { id: generateID(), title: 'Write React Hooks book', completed: true }, { id: generateID(), title: 'Promote book', completed: false }, ]), 100 ) )现在,我们有一个函数,通过在 100 毫秒的延迟后返回一个数组来模拟从 API 获取待办事项。定义状态上下文接下来,我们将定义一个上下文,该上下文将保留我们当前的待办事项列表。我们将此上下文称 StateContext。现在让我们开始实现StateContext:创建一个新的 src/StateContext.js 文件。导入 React,如下所示:import React from 'react'现在,定义 StateContext 并将一个空数组设置为回退值:const StateContext = React.createContext([])最后,导出 StateContext:导出默认状态上下文 现在,我们有一个上下文,我们可以在其中存储待办事项数组。使应用组件动态化我们现在将通过添加获取、添加、切换、过滤和删除待办事项的功能,使 App 组件动态化。此外,我们将定义一个 StateContext 提供程序。让我们开始使 App 组件动态化:在 src/App.js 中,导入 StateContext,在其他导入语句之后:import StateContext from './StateContext'然后,从 src/api.js 文件中导入 fetchAPiTodos 和 generateiD 函数:import { fetchAPiTodos, generateiD } from './api'接下来,我们将修改我们的 App 类代码,实现一个constructor,它将设置初始状态:export default class App extends React.Component { constructor (props) {在这个 constructor 中,我们需要首先调用 super,以确保父类(React.Component)构造函数被调用,并且组件被正确初始化:super(props)现在,我们可以通过设置 this.state 来设置初始状态。最初,将没有待办事项,filter 值将设置为all: this.state = { todos: [], filteredTodos: [], filter: 'all' } }然后,我们定义 componentDidMount 生命周期方法,该方法将在组件首次呈现时获取待办事项:componentDidMount () { this.fetchTodos() }现在,我们将定义实际的 fetchTodos 方法,在我们的例子中,它只是设置状态,因为我们不打算将这个简单的应用程序连接到后端。我们还将调用 this.filterTodos(),以便在获取 todos 后更新 filteredTodos 数组:fetchTodos () { fetchAPiTodos().then((todos) => { this.setState({ todos }) this.filterTodos() }) }接下来,我们定义 addTodo 方法,该方法创建一个新项,并将其添加到状态数组中,类似于我们在博客应用程序中使用 Hooks 所做的:addTodo (title) { const { todos } = this.state const newTodo = { id: generateiD(), title, completed: false } this.setState({ todos: [ newTodo, ...todos ] }) this.filterTodos() }然后,我们定义 toggleTodo 方法,该方法使用 map 函数来查找和修改某个待办事项:toggleTodo (id) { const { todos } = this.state const newTodos = todos.map(t => { if (t.id === id) { return { ...t, completed: !t.completed } } return t }, []) this.setState({ todos: newTodos }) this.filterTodos() }现在,我们定义 removeTodo 方法,它使用 filter 函数来查找和删除某个待办事项:removeTodo (id) { const { todos } = this.state const newTodos = todos.filter(t => { if (t.id === id) { return false } return true }) this.setState({ todos: newTodos }) this.filterTodos() }然后,我们定义一种方法来将某个 filter 应用于我们的待办事项:applyFilter(todos, filter) { switch (filter) { case 'active': return todos.filter((t) => t.completed === false) case 'completed': return todos.filter((t) => t.completed === true) default: case 'all': return todos } }现在,我们可以定义 filterTodos 方法,它将调用 applyFilter 方法,并更新 filteredTodos 数组和 filter 值:filterTodos(filterArg) { this.setState(({ todos, filter }) => ({ filter: filterArg || filter, filteredTodos: this.applyFilter(todos, filterArg || filter) })) }我们正在使用过滤器待办事项,以便在添加/删除项目以及更改过滤器后重新过滤待办事项。为了使这两个功能正常工作,我们需要检查过滤器参数 filterArg 是否已传递。如果没有,我们从状态回退到当前过滤器参数。然后,我们调整 render 方法,以便使用 state 为 StateContext 提供值,并将某些方法传递给组件:render () { const { filter, filteredTodos } = this.state return ( <Statecontext.Provider value={filteredTodos}>最后,我们需要将 this 重新绑定到类,以便我们可以将方法传递给我们的组件,而无需更改 this 上下文。按如下方式调整constructor:constructor() { super(props) this.state = { todos: [], filteredTodos: [], filter: 'all' } this.fetchTodos = this.fetchTodos.bind(this) this.addTodo = this.addTodo.bind(this) this.toggleTodo = this.toggleTodo.bind(this) this.removeTodo = this.removeTodo.bind(this) this.filterTodos = this.filterTodos.bind(this) }现在,我们的 App 组件可以动态获取、添加、切换、删除和过滤待办事项。如我们所见,当我们使用类组件时,我们需要重新绑定 thiscontex。的处理程序函数到类。使 AddTodo 组件动态化在使我们的 App 组件动态化之后,是时候将所有其他组件动态化为 wel1 了。我们将从顶部开始,使用 AddTodo 组件。现在让我们使 AddTodo 组件动态化:在 src/AddTodo.js 中,我们首先定义了一个 constructor,它为 input 字段设置初始 state:export default class AddTodo extends React.Component { constructor (props) { super(props) this.state = { input: ''} }然后,我们定义一个处理 input 字段中更改的方法:handleinput (e) { this.setState({ input: e.target.value }) }现在,我们将定义一个可以处理正在添加的新待办事项的方法:handleAdd () { const { input } = this.state const { addTodo } = this.props if (input) { addTodo(input) this.setState({ input: '' }) } }接下来,我们可以将状态值和处理程序方法分配给 input 字段和按钮:render() { const { input } = this.state return ( <form onSubmit={e => { e.preventDefault(); this.handleAdd( )}}> <input type="text" placeholder="enter new task..." style={{ width: 350, height: 15 }} value={input} onChange={this.handleInput} /> <input type="submit" style={{ float: 'right', marginTop: 2 }} disabled={!input} value="add" /> </form> ) }最后,我们需要调整 constructor 以重新绑定 thisconstructor() { super(props) this.state = { input: '' } this.handleInput = this.handleInput.bind(this) this.handleAdd = this.handleAdd.bind(this) }现在,只要不输入文本,我们的 AddTodo 组件就会显示一个禁用的按钮。激活后,单击该按钮将触发从 App 组件向下传递的 handleAdd 功能。使待办事项列表组件动态化我们的 ToDo 应用程序中的下一个组件是 TodoList 组件。在这里,我们只需要从 StateContext 中获取待办事项。现在让我们使 TodoList 组件动态化:在 src/TodoList.js 中,我们首先导入 StateContext,位于 Todoitem 导入语句下方:import StateContext from './StateContext'然后,我们将 contextType 设置为 StateContext,这将允许我们通过 this.context 访问上下文:export default class TodoList extends React.Component { static contextType = Statecontext对于类组件,如果我们想使用多个上下文,我们必须使用 StateContext.Consumer 组件,如下所示:<StateContext.Consumer>{value => <div>State is:{value}</div>}</StateContext.Consumer>可以想象,使用这样的多个上下文将导致一个非常深的组件树(包装器地狱),并且我们的代码将难以阅读和重构。现在,我们可以从 this.context 获取项目,而不是静态定义它们:render () { const items = this.context最后,我们将所有 props 传递给 Todoitem 组件,以便我们可以在那里使用 removeTodo 和 toggleTodo 方法:return items.map(item => <Todoitem {...item} {...this.props} key={item.id} /> ) }现在,我们的 TodoList 组件从 StateContext 中获取项目,而不是静态定义它们。使待办事项组件动态化现在我们已经将 removeTodo 和 toggleTodo 方法作为 props 传递给了 Todoitem 组件,我们可以在那里实现这些功能。现在让我们使 Todoitem 组件动态化:在 src/Todoitem.js 中,我们首先定义 toggleTodo 和 removeTodo 函数的处理程序方法:handleToggle () { const { toggleTodo, id } = this.props toggleTodo(id) } handleRemove () { const { removeTodo, id } = this.props removeTodo(id) }然后,我们将处理程序方法分别分配给 checkbox 和 button:render() { const { title, completed } = this.props return ( <div style={{ width: 400, height: 25 }}> <input type="checkbox" checked={completed} onChange= {this.handleToggle} /> {title} <button style={{ float: 'right' }} onClick= {this.handleRemove}>x</button> </div> ) }最后,我们需要重新绑定处理程序方法的 this 上下文。创建一个新的constructor,如下所示:export default class Todoitem extends React.Component { constructor (props) { super(props) this.handleToggle = this.handleToggle.bind(this) this.handleRemove = this.handleRemove.bind(this) }现在,Todoitem 组件触发切换和删除处理程序函数。使 TodoFilter 组件动态化最后,我们将使用 filterTodos 方法来动态过滤我们的待办事项列表。让我们开始使 TodoFilter 组件动态化:在 src/TodoFilter.js 的 TodoFilter 类中,我们将所有 props 传递给 TodoFilteritem 组件:export default class TodoFilter extends React.Component { render() { return ( <div> <TodoFilterItem {...this.props} name="all" /> {' / '} <TodoFilterItem {...this.props} name="active" /> {' / '} <TodoFilterItem {...this.props} name="completed" /> </div> ) } }在 src/TodoFilter.js 的 TodoFilteritem 类中,我们首先定义一个用于设置过滤器的处理程序方法:handleFilter () { const { name, filterTodos } = this.props filterTodos(name) }然后我们从 TodoFilter 中获取 filter 属性:render () { const { name`, filter = 'all'` } = this.props接下来,我们使用 filter 属性以 bold 显示当前选择的过滤器:const style = { color: 'blue', cursor: 'pointer', fontWeight: filter === name ? 'bold' : 'normal', }然后,我们通过 onClick 将处理程序方法绑定到筛选项:return <span style={style} onClick={this.handleFilter}>{name} </span> }最后,我们为 TodoFilteritem 类创建一个新的 constructor,并重新绑定 this 上下文class TodoFilterItem extends React.Component { constructor(props) { super(props) this.handleFilter = this.handleFilter.bind(this) } }现在,我们的 TodoFilter 组件触发了 handleFilter 方法以更改过滤器。我们的整个应用程序现在是动态的,我们可以使用它的所有功能。示例代码示例代码可以在 Chapter11/chapter11_1 文件夹中找到。只需运行 npm install 以安装所有依赖项,并运行 npm start 以启动应用程序,然后访问 http://localhost:3000(如果它没有自动打开)。
2021年05月06日
1 阅读
0 评论
0 点赞
2021-04-28
第 10 章创建自己的 Hooks
在上一章中,我们了解了 Hooks 的限制和规则。我们学习了在哪里称呼 Hooks,为什么 Hooks 的顺序很重要,以及 Hooks 的命名约定。最后,我们学习了如何执行 Hooks 的规则和处理 useEffect 依赖关系。在本章中,我们将学习如何通过从组件中提取现有代码来创建自定义 Hooks。我们还将学习如何使用自定义 Hooks 以及 Hooks 如何相互交互。然后,我们将学习如何为我们的自定义 Hooks 编写测试。最后,我们将了解完整的 React Hooks API。本章将介绍以下主题:提取自定义 Hooks使用自定义 HooksHooks 之间的交互测试 Hooks探索 React Hooks API技术要求应该已经安装了相当新版本的 Node.js(vll.l2.0 或更高版本)。Node.js 的 npm 包管理器也需要安装。本章的代码可以在 GitHub 存储库中找到:https://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter10.观看以下视频,了解代码的实际应用:http://bit.ly/2Mm9yoC请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。为了正确学习和理解,自己编写代码很重要。但是,如果遇到任何问题,始终可以参考代码示例。提取自定义 Hooks在通过了解状态和效果 Hooks、社区 Hooks 和 Hooks 的规则很好地掌握了 Hooks 的概念后,我们现在要创建自己的 Hooks。我们首先从博客应用的现有功能中提取自定义 Hooks。通常,如果我们注意到我们在多个组件中使用类似的代码,则首先编写组件,然后从中提取自定义 Hook 是最有意义的。这样做可以避免过早地定义自定义 Hooks 并使我们的项目变得不必要地复杂。我们将在本节中提取以下 Hooks:useTheme HookuseUserState 和 usePostsState HooksuseDispatch HookAPI HooksuseDebouncedUndo Hook创建 useTheme Hook在许多组件中,我们使用 ThemeContex 来设置博客应用的样式。跨多个组件使用的功能通常是创建自定义 Hook 的好机会。您可能已经注意到,我们经常执行以下操作:import { ThemeContext } from '../contexts' export default function SomeComponent () { const theme = useContext(ThemeContext) // ...我们可以将此功能抽象为 useTheme Hook,它将从 ThemeContext 中获取 theme 对象。让我们开始创建一个自定义的 useTheme Hook:创建一个新的 src/hooks/ 目录,我们将在其中放置自定义的 Hooks。创建一个新的 src/hooks/useTheme.js 文件。在这个新创建的文件中,我们首先导入 useContext Hook 和 ThemeContext,如下所示:import { useContext } from 'react' import { ThemeContext } from '../contexts'接下来,我们导出一个名为 useTheme 的新函数;这将是我们的自定义 Hook。请记住, Hooks 只是以 use 关键字为前缀的函数:export default function useTheme () {在我们的自定义 Hook 中,我们现在可以使用 React 提供的基本 Hooks 来创建我们自己的 Hook。在我们的例子中,我们只是返回 useContext Hook: return useContext(ThemeContext) }如我们所见,自定义 Hooks 可以非常简单。在这种情况下,自定义 Hook 只返回一个 Content Hook,并将 ThemeContext 传递给它。尽管如此,这使得我们的代码更简洁,以后更容易更改。此外,通过使用 useTheme Hook,很明显我们想要访问主题,这意味着我们的代码将更容易阅读和推理。创建全局状态 Hooks我们经常做的另一件事是访问全局状态。例如,某些组件需要 user 状态,而某些组件需要 posts 状态。为了抽象这个功能,这也将使以后更容易调整状态结构,我们可以创建自定义的 Hooks 来获取状态的某些部分:useUserState:获取 state 对象的 user 部分usePostsState:获取 state 对象的 posts 部分定义 useUserState Hook重复与我们对 useTheme Hook 所做的类似的过程,我们从 React 和 StateContext 导入 useContext Hook。但是,我们现在不是返回 Content Hook 的结果,而是通过解构提取 state 对象,然后返回 state.user。创建一个新的 src/hooks/useUserState.js 文件,其中包含以下内容:import { useContext } from 'react' import { StateContext } from '../contexts' export default function useUserState() { const { state } = useContext(StateContext) return state.user }与 useTheme Hook 类似,useUserState Hook 使我们的代码更简洁,以后更容易更改,并提高可读性。定义 usePostsState Hook我们对 posts 状态重复相同的过程。创建一个新的 src/hooks/usePostsState.js 文件,其中包含以下内容:import { useContext } from 'react' import { StateContext } from '../contexts' export default function usePostsState () { const { state } = useContext(StateContext) r eturn state.posts }与 useTheme 和 useUserState Hooks 类似,usePostsState Hook 使我们的代码更简洁,以后更容易更改,并提高可读性。创建 useDispatch Hook在许多组件中,我们需要 dispatch 函数来执行某些 actions,因此我们经常不得不执行以下操作:import { StateContext } from '../contexts' export default function SomeComponent () { const { dispatch } = useContext(StateContext) // ...我们可以将此功能抽象为 useDispatch Hook,它将从我们的全局状态上下文中获取 dispatch 函数。这样做还可以使以后更容易替换状态管理实现。例如,稍后,我们可以将简单的 Reducer Hook 替换为状态管理库,例如 Redux 或 MobX。现在让我们使用以下步骤定义 useDispatch Hook:创建一个新的 src/hooks/useDispatch.js 文件。从 React 导入 useContext Hook 和 StateContext,如下所示:import { useContext } from 'react' import { StateContext } from '../contexts'接下来,我们定义并导出 useDispatch 函数;在这里,我们允许传递不同的 context 作为使 Hook 更通用的参数(以防我们稍后想从本地状态上下文中使用 dispatch 函数)。但是,我们将 context 参数的默认值设置为 StateContext,如下所示:export default function useDispatch (context = StateContext) {最后,我们通过解构从 Content Hook 中提取 dispatch 函数,并使用以下代码返回它:const { dispatch } = useContext(context) return dispatch }正如我们所看到的,创建自定义的 Dispatch Hook 使我们的代码在以后更容易更改,因为我们只需要在一个地方调整 dispatch 函数。创建 API Hooks我们还可以为各种 API 调用创建 Hooks。将这些 Hooks 放在一个文件中,使我们能够在以后轻松调整 API 调用。我们将在自定义 API Hooks 前面加上 useAPI,以便很容易分辨哪些函数是 API Hooks。现在,让我们使用以下步骤为我们的 API 创建自定义 Hooks:创建一个新的 src/hooks/api.js 文件。从 react-request-hook 库中导入 useResource Hook,如下所示:import { useResource } from 'react-request-hook'首先,我们定义一个 useAPILogin Hook 来登录用户;我们只需从 src/user/Login.js 文件中剪切并粘贴现有代码,如下所示:export function useAPILogin () { return useResource((username, password) => { url: '/login/${encodeURI(username)}/${encodeURI(password)}', method: 'get'. }) }接下来,我们定义一个 useAPIRegister Hook;我们只需从 src/user/Register.js 文件中剪切并粘贴现有代码,如下所示:export function useAPIRegister () { return useResource((username, password) => { url: '/users', method: 'post', data: { username, password } }) }现在我们定义一个 useAPICreatePost Hook,从 src/post/CreatePost.js 文件中剪切并粘贴现有代码,如下所示:export function useAPICreatePost () { return useResource(({ title, content, author }) => { url: '/posts', method: 'post', data: { title, content, author } }) }最后,我们定义一个 useAPIThemes Hook,从 src/ChangeTheme.js 文件中剪切并粘贴现有代码,如下所示:export function useAPIThemes () { return useResource(() => { url: '/themes', method: 'get' }) }正如我们所看到的,将所有与 API 相关的功能放在一个地方可以更轻松地在以后调整我们的 API 代码。创建 useDebouncedUndo Hook我们现在将创建一个稍微高级的 Hook,用于去抖动撤消功能。我们已经在 CreatePost 组件中实现了此功能。现在,我们将此功能提取到自定义的 useDebouncedUndo Hook 中。让我们通过以下步骤创建 useDebouncedUndo Hook:创建一个新的 src/hooks/useDebouncedUndo.js 文件。从 React 导入 useState、useEffect 和 useCallback Hooks,以及 useUndo Hook 和 useDebouncedCallback Hook:import { useState, useEffect, useCallback } from 'react' import useUndo from 'use-undo' import { useDebouncedCallback } from 'use-debounce'现在我们将定义 useDebouncedUndo 函数,它接受去抖回调的 timeout 参数:export default function useDebouncedUndo (timeout = 200) {在这个函数中,我们从前面的实现中复制了 useState Hook,如下所示:const [content, setInput] = useState('')接下来,我们复制 useUndo Hook;但是,这一次,我们将所有其他与撤消相关的函数存储在 undoRest 对象中:const [undoContent, { set: setContent, ...undoRest }] = useUndo('')然后我们复制 useDebouncedCallback Hook,用我们的 timeout 参数替换固定的 200 值:const [setDebounce, cancelDebounce] = useDebouncedCallback((value) => { setContent(value) }, timeout)现在我们复制效果 Hook,如以下代码所示:useEffect(() => { cancelDebounce() setInput(undoContent.present) }, [cancelDebounce, undoContent])然后,我们定义一个 setter 函数,它将设置一个新的输入 value 并调用 setDebounce。我们可以在这里用 useCallback Hook 包装 setter 函数,以返回函数的记忆版本,并避免每次使用 Hook 的组件重新渲染时重新创建函数。与 useEffect 和 useMemo Hooks 类似,我们也传递了一个依赖数组作为 useCallback Hook 的第二个参数:const setter = useCallback( function setterFn(value) { setInput(value) setDebounce(value) }, [setInput, setDebounce] )最后,我们返回 content 变量(包含当前输入的 value)、setter 函数和 undoRest 对象(其中包含 undo/redo 函数和 canUndo/canRedo 布尔值):return [ content, setter, undoRest ] }为去抖动撤消创建自定义 Hook 意味着我们可以在多个组件中重用该功能。我们甚至可以将此 Hook 作为公共库提供,允许其他人轻松实现去反弹撤消/重做功能。导出我们的自定义 Hooks创建所有自定义 Hooks 后,我们将在 Hooks 目录中创建一个 index.js 文件,并在那里重新导出我们的 Hooks,以便我们可以导入自定义 Hooks,如下所示:import { useTheme } from './hooks'现在让我们使用以下步骤导出所有自定义 Hooks:创建一个新的 src/hooks/index.js 文件。在此文件中,我们首先导入自定义的 Hooks,如下所示:import useTheme from './useTheme' import useDispatch from './useDispatch' import usePostsState from './usePostsState' import useUserState from './useUserState' import useDebouncedUndo from './useDebouncedUndo'然后,我们使用以下代码重新导出这些导入的 Hooks:export { useTheme, useDispatch, usePostsState, useUserState, useDebouncedUndo }最后,我们从 api.js 文件中重新导出所有 Hooks,如下所示:export * from './api'现在我们已经导出了所有自定义 Hooks,我们可以简单地直接从 Hooks 文件夹中导入 Hooks,从而更容易一次导入多个自定义 Hooks。示例代码示例代码可以在 Chapter10/chapter10_1 文件夹中找到。只需运行 npm install 即可安装所有依赖项,并运行 npm start 即可启动应用,然后访问 http://localhost:3000(如果它没有自动打开)。使用我们的自定义 Hooks创建自定义 Hooks 后,我们现在可以开始在整个博客应用中使用它们。使用自定义 Hooks 非常简单,因为它们类似于社区 Hooks。就像所有其他 Hooks 一样,自定义 Hooks 只是 JavaScript 函数。我们创建了以下 Hooks:useThemeuseDispatchusePostsStateuseUserStateuseDebouncedUndouseAPILoginuseAPIRegisteruseAPICreatePostuseAPIThemes在本节中,我们将重构我们的应用以使用我们所有的自定义 Hooks。使用 useTheme Hook现在,我们可以直接使用 useTheme Hook,而不是将 useContext Hook 与 ThemeContext 一起使用!如果我们以后最终更改了主题系统,我们可以简单地修改 useTheme Hook,我们的新系统将在整个应用中实现。让我们重构我们的应用以使用 useTheme Hook:编辑 src/Header.js 并将现有导入替换为 useTheme Hook 的导入。可以删除 ThemeContext 和 useContext 导入:import { useTheme } from './hooks'然后,将当前的 Content Hook 定义替换为 useTheme Hook,如下所示:const { primaryColor } = useTheme()现在编辑 src/post/Post.js 并在那里类似地调整导入:import { useTheme } from './hooks'然后,将 useContext Hook 替换为 useTheme Hook,如下所示:const { secondaryColor } = useTheme()正如我们所看到的,使用自定义 Hook 使我们的代码更加简洁和易于阅读。现在我们继续使用全局状态 Hooks。使用全局状态 Hooks与我们对 ThemeContext 所做的类似,我们也可以将状态上下文 Hooks 替换为 usePostsState,useUserState 和 useDispatch Hooks。如果我们以后想更改状态逻辑,这是最佳的。例如,如果我们的状态增长,并且我们想使用更复杂的系统,如 Redux 或 MobX,那么我们可以简单地调整现有的 Hooks,一切都会像以前一样工作。在本节中,我们将调整以下组件:UserBarLoginRegisterLogoutCreatePostPostList调整 UserBar 组件首先,我们将调整 UserBar 组件。在这里,我们可以按照以下步骤使用 useUserState Hook:编辑 src/user/UserBar.js 并导入 useUserState Hook:import { useUserState } from '../hooks'然后,我们删除以下 Hook 定义:const { state } = useContext(StateContext) const { user } = state我们用自定义的 useUserState Hook 替换它:const user = useUserState()现在,UserBar 组件使用我们的自定义 Hook,而不是直接访问 user 状态。调整 Login 组件接下来,我们将调整 Login 组件,我们可以在其中使用 useDispatch Hook。以下步骤概述了此过程:编辑 src/user/Login.js 并导入 useDispatch Hook,如下所示:import { useDispatch } from '../hooks'然后删除以下 Content Hook:const { dispatch } = useContext(StateContext)将其替换为我们自定义的 useDispatch Hook:const dispatch = useDispatch()现在,Login 组件使用我们的自定义 Hook,而不是直接访问 dispatch 函数。接下来,我们将调整 Register 组件。调整 Register 组件与 Login 组件类似,我们也可以在 Register 组件中使用 useDispatch Hook,如下步骤所示:编辑 src/user/Register.js 并导入 useDispatch Hook:import { useDispatch } from '../hooks'然后,将当前的 Content Hook 替换为我们的自定义 Dispatch Hook,如下所示:const dispatch = useDispatch()现在,Register 组件也使用我们的自定义 Hook,而不是直接访问 dispatch 函数。调整 Logout 组件然后,我们将调整 Logout 组件以同时使用 useUserState 和 useDispatch Hooks,步骤如下:编辑 src/user/Logout.js 并导入 useUserState 和 useDispatch Hooks:import { useDispatch, useUserState } from '../hooks'然后,将当前的 Hook 定义替换为以下内容:const dispatch = useDispatch() const user = useUserState()现在,Logout 组件使用我们的自定义 Hooks,而不是直接访问 user 状态和 dispatch 函数。调整 CreatePost 组件接下来,我们将调整 CreatePost 组件,它类似于我们对 Logout 组件所做的。以下步骤概述了此过程:编辑 src/post/CreatePost.js 并导入 useUserState 和 useDispatch Hooks:import { useUserState, useDispatch } from '../hooks'然后,将当前的 Content Hook 定义替换为以下内容:const user = useUserState() const dispatch = useDispatch()现在,CreatePost 组件使用我们的自定义 Hooks,而不是直接访问 user 状态和 dispatch 函数。调整 PostList 组件最后,我们将使用 usePostsState Hook 来渲染 PostList 组件,如下所示:编辑 src/post/PostList.js 并导入 usePostsState Hook:import { usePostsState } from '../hooks'然后将当前的 Hook 定义替换为以下内容:const posts = usePostsState()现在,PostList 组件使用我们的自定义 Hook,而不是直接访问 posts 状态。使用 API Hooks接下来,我们将用我们的自定义 API Hooks 替换所有 useResource Hooks。这样做允许我们将所有 API 调用放在一个文件中,以便我们以后可以轻松调整它们,以防 API 发生变化。在本节中,我们将调整以下组件:ChangeThemeRegisterLoginCreatePost让我们开始吧。调整 ChangeTheme 组件首先,我们将调整 ChangeTheme 组件并替换资源 Hook,在以下步骤中用我们自定义的 useAPIThemes Hook 访问 /themes:在 src/ChangeTheme.js 中,删除以下 useResource Hook import 语句:import { useResource } from 'react-request-hook'将其替换为我们自定义的 useAPIThemes Hook:import { useAPIThemes } from './hooks'然后,将 useResource Hook 定义替换为以下自定义 Hook :const [themes, getThemes] = useAPIThemes()现在,ChangeTheme 组件使用我们的自定义 API Hook 从 API 中提取主题。调整 Register 组件接下来,我们将通过以下步骤调整 Register 组件:编辑 src/user/Register.js 并调整 import 语句以导入 useAPIRegister Hook:import { useDispatch, useAPIRegister } from '../hooks'然后,将当前 Resouce Hook 替换为以下内容:const [user, register] = useAPIRegister()现在,Register 组件使用我们的自定义 API Hook 通过 API register 用户。调整 Login 组件与 Register 组件类似,我们还将调整 Login 组件:编辑 src/user/Login.js 并调整导入语句以导入 useAPILogin Hook:import { useDispatch, useAPILogin } from '../hooks'然后,将当前 Resouce Hook 替换为以下内容:const [user, login] = useAPILogin()现在,Login 组件使用我们的自定义 API Hook 通过 API 登录用户。调整 CreatePost 组件最后,我们将按照以下步骤调整 CreatePost 组件:编辑 src/post/CreatePost.js 并调整导入语句以导入 useAPICreatePost Hook:import { useUserState, useDispatch, useAPICreatePost } from '../hooks'然后,将当前 Resouce Hook 替换为以下内容:const [post, createPost] = useAPICreatePost()现在,CreatePost 组件使用我们的自定义 API Hook 通过 API 创建新帖子。使用 useDebouncedUndo Hook最后,我们将用自定义的 useDebouncedUndo Hook 替换 src/post/CreatePost.js 文件中的所有去抖动撤消逻辑。这样做将使我们的组件代码更清晰,更易于阅读。此外,我们稍后可以在其他组件中重用相同的去抖动撤消功能。让我们按照以下步骤开始使用 CreatePost 组件中的去抖动撤消 Hook :编辑 src/post/CreatePost.js 并导入 useDebouncedUndo Hook:import { useUserState, useDispatch, useDebouncedUndo, useAPICreatePost } from '../hooks'然后,删除以下与去抖动撤消处理相关的代码:const [content, setInput] = useState('') const [undoContent, { set: setContent, undo, redo, canUndo, canRedo }] = useUndo('') const [setDebounce, cancelDebounce] = useDebouncedCallback((value) => { setContent(value) }, 200) useEffect(() => { cancelDebounce() setInput(undoContent.present) }, [cancelDebounce, undoContent])将其替换为我们自定义的 useDebouncedUndo Hook,如下所示:const [content, setContent, { undo, redo, canUndo, canRedo }] = useDebouncedUndo()最后,在我们的 handleContent 函数中删除以下 setter 函数(以粗体标记):function handleContent (e) { const { value } = e.target setInput(value) setDebounce(value) }我们现在可以使用自定义 Hook 提供的 setContent 函数:function handleContent (e) { const { value } = e.target setContent(value) } 如您所见,我们的代码现在更干净、更简洁、更易于阅读。此外,我们稍后可以在其他组件中重用去抖动的撤消 Hook。示例代码示例代码可以在 Chapter10/chapter10_2 文件夹中找到。只需运行 npm install 即可安装所有依赖项,并运行 npm start 即可启动应用,然后访问 http://localhost:3000(如果它没有自动打开)。(节选)
2021年04月28日
1 阅读
0 评论
1 点赞
2021-04-08
第 9 章 Hook 的规则
在上一章中,我们学习了如何使用 React 社区开发的各种 Hooks,以及在哪里可以找到更多。我们学习了用 Hooks 替换 React 生命周期方法,用 Hook 替换实用程序和数据管理 Hooks,用 Hooks 替换响应式设计,以及用 Hooks 实现撤消/重做功能。最后,我们学习了在哪里可以找到其他 Hooks。在本章中,我们将学习有关使用 Hooks 的所有知识,以及使用和开发我们自己的 Hooks 时需要注意的事项。 Hooks 对它们的调用顺序有一定的限制。违反 Hooks 的规则可能会导致错误或意外行为,因此我们需要确保我们学习并执行这些规则。本章将介绍以下主题:调用 HooksHooks 的顺序Hooks 的名称执行 Hooks 的规则处理 useEffect依赖关系技术要求应该已经安装了相当新版本的 Node.js(vll.l2.0 或更高版本)。Node.js 的 npm 包管理器也需要安装。本章的代码可以在 GitHub 存储库中找到:https://github.com/PacktPublishing/Learn-React-Hooks/tree/master/Chapter09.观看以下视频,了解代码的实际应用:http://bit.ly/2Mm9yoC请注意,强烈建议您自己编写代码。不要简单地运行已提供的代码示例。为了正确学习和理解,自己编写代码很重要。但是,如果遇到任何问题,始终可以参考代码示例。现在,让我们从本章开始。调用 HooksHooks 应该只在 React 函数组件或自定义 Hooks 中调用。它们不能在类组件或常规 JavaScript 函数中使用。可以在以下级别的顶层调用 Hooks :React 函数组件自定义 Hooks(我们将在下一章中学习如何创建自定义 Hooks)正如我们所看到的,Hooks 大多是普通的 JavaScript 函数,除了它们依赖于在 React 函数组件中定义。当然,使用其他 Hooks 的自定义 Hooks 可以在 React 函数组件之外定义,但是在使用 Hooks 时,我们始终需要确保在 React 函数组件中调用它们。接下来,我们将了解有关 Hooks 顺序的规则。Hooks 的顺序仅在函数组件或自定义 Hooks 的顶层开头调用 Hooks。不要在条件、循环或嵌套函数中调用 Hooks——这样做会改变 Hooks 的顺序,从而导致错误。我们已经了解到,更改 Hooks 的顺序会导致状态在多个 Hooks 之间混淆。在第 2 章“使用 State Hook”中,我们了解到我们不能执行以下操作:const [ enableFirstName, setEnableFirstName ] = usestate(false) const [ name, setName ] = enableFirstName ? usestate('') : [ '', () => {} ] const [ lastName, setLastName ] = usestate('')我们为 firstName 和 lastName 渲染了一个复选框和两个输入字段,然后在 lastName 字段中输入了一些文本:重温第 2 章“使用 State Hook” 中的示例目前,Hooks 的顺序如下:enableFirstNamelastName接下来,我们单击复选框以启用 firstName 字段。这样做改变了 Hooks 的顺序,因为现在我们的 Hooks 定义如下所示:enableFirstNamefirstNamelastName由于 React 完全依靠 Hooks 的顺序来管理它们的状态,所以 firstName 字段现在是第二个 Hook,所以它从 lastName 字段中获取状态:改变第 2 章“使用 State Hook”中 Hooks 顺序的问题如果我们在示例 2 中使用来自 React 的真正 usestate Hook “我们可以定义条件 Hooks 吗”?从 第 2 章 “使用 State Hook”中,我们可以看到 React 会自动检测 Hooks 的顺序何时发生变化,并且会显示警告:React 在检测到 Hooks 的顺序已更改时打印警告在开发模式下运行 React 时,当渲染比之前的渲染更多的 Hook 时,它还会崩溃并显示未捕获的不变违规错误消息:当 Hooks 数量发生变化时,在开发模式下 React 崩溃正如我们所看到的,更改 Hooks 的顺序或有条件地启用 Hooks 是不可能的,因为 React 内部使用 Hooks 的顺序来跟踪哪些数据属于哪个 Hook。Hooks 的名称有一个约定,Hook 函数应始终以 use 为前缀,后跟以大写字母开头的 Hook 名称;例如: usestate 、 useEffect 和 useResource。这很重要,因为否则我们将不知道哪些 JavaScript 函数是 Hooks,哪些不是。特别是在执行 Hooks 的规则时,我们需要知道哪些函数是 Hooks,这样我们才能确保它们不会被有条件地或循环调用。正如我们所看到的,命名约定在技术上不是必需的,但它们使开发人员的生活变得更加轻松。了解普通函数和 Hooks 之间的区别使得自动执行 Hooks 的规则变得非常容易。在下一节中,我们将学习如何使用 eslint 工具自动执行规则。执行 Hooks 的规则如果我们坚持在 Hook 函数前面加上 use 的约定,我们可以自动执行其他两个规则:仅从 React 函数组件或自定义 Hooks 调用 Hooks仅在顶层调用 Hooks(不在循环、条件或嵌套函数内部)为了自动执行规则,React 提供了一个名为 eslint-plugin-react-hooks 的 eslint 插件,它会自动检测何时使用 Hooks,并确保规则不被破坏。ESLint 是一个 linter,它是一种分析源代码并发现诸如风格错误、潜在错误和编程错误等问题的工具。将来,create-react-app 将默认包含此插件。设置 eslint-plugin-react-hooks我们现在将设置 React Hooks eslint 插件来自动执行 Hooks 的规则。让我们开始安装和启用 eslint 插件:首先,我们必须通过 npm 安装插件:npm install --save-dev eslint-plugin-react-hooks我们在这里使用 --save-dev 标志,因为在部署应用程序时不需要安装 eslint 及其插件。我们只需要在开发应用程序期间使用它们。然后,我们在项目文件夹的根目录中创建一个新的 .eslintrc.json 文件,其中包含以下内容。我们从 react-app ESLint 配置开始扩展:{ "extends": "react-app",接下来,我们包括我们之前安装的 react-hooks 插件:"plugins": [ "react-hooks" ],现在我们启用两个规则。首先,我们告诉 eslint 在我们违反 rules-of-hooks 规则时显示错误。此外,我们启用 exhaustive-deps 规则作为警告: "rules": { "react-hooks/rules-of-hooks": "error", "react-hooks/exhaustive-deps": "warn" } }最后,我们调整 package.json 来定义一个新的 lint 脚本,它将调用 eslint:"scripts": { "lint": "npx eslint src/",现在,我们可以执行 npm run lint,我们将看到有 5 个警告和 0 个错误:使用 react-hooks 插件执行 ESLint我们现在将尝试打破 Hooks 的规则;例如,通过编辑 src/user/Login.js 并使第二个输入 Hook 成为条件:const { value: password, bindToinput: bindPassword } = loginFailed ? useinput('') : [ '', () => {} ]当我们再次执行 npm run lint 时,我们可以看到现在有一个错误:打破 Hooks 规则后执行 ESLint正如我们所看到的,eslint 通过迫使我们遵守 Hooks 的规则来帮助我们。当我们违反任何规则时,linter 会抛出错误,并在 Effect Hooks 缺少依赖项时显示警告。听 eslint 将帮助我们避免错误和意外行为,因此我们永远不应该忽视它的错误或警告。示例代码示例代码可以在 Chapter09/chapter9_1 文件夹中找到。只需运行 npm install 即可安装所有依赖项并执行 npm run lint 来运行linter。处理 useEffect 依赖关系除了强制执行 Hooks 的规则之外,我们还在检查 Effect Hook 中使用的所有变量是否都传递给了它的依赖数组。这种详尽的依赖关系规则可确保每当 Effect Hook 中使用的内容(函数、值等)发生变化时,Hook 将再次触发。正如我们在上一节中看到的,在使用 npm run lint 运行 linter 时,有几个与穷举依赖关系规则相关的警告。通常,它与 dispatch 函数或其他不属于依赖数组的函数有关。通常,这些功能不应该改变,但我们永远无法确定,所以最好将它们添加到依赖项中。使用 ESLint 自动修复警告由于详尽的依赖关系规则非常简单明了,我们可以自动让 eslint 修复它。为此,我们需要将 --fix 标志传递给 eslint。使用 npm run,我们可以通过使用额外的 -- 作为分隔符来传递标志,如下所示:npm run lint --fix运行上述命令后,我们可以再次运行 npm run lint,我们将看到所有警告都已自动修复:让 ESLint 修复它们后没有警告正如我们所看到的,eslint 不仅可以警告我们问题,甚至可以自动为我们修复其中一些问题!示例代码示例代码可以在 Chapter09/chapter9_2 文件夹中找到。只需运行 npm install 即可安装所有依赖项并执行 npm run lint 来运行linter。总结在本章中,我们首先了解了 Hooks 的两个规则:我们应该只从 React 函数组件调用 Hooks,并且我们需要确保 Hooks 的顺序保持不变。此外,我们了解了 Hooks 的命名约定,它们应该始终以 use 前缀开头。然后,我们学习了如何使用 eslint 执行 Hooks 的规则。最后,我们了解了 useEffect 依赖项,以及如何使用 eslint 自动修复缺少的依赖项。了解 Hooks 的规则并执行它们对于避免错误和意外行为非常重要。在创建我们自己的 Hooks 时,这些规则尤其重要。现在我们已经很好地掌握了 Hooks 的工作原理,包括它们的规则和约定,在下一章中,我们将学习如何创建自己的 Hooks!问题为了回顾我们在本章中学到的内容,请尝试回答以下问题:Hooks 可以在哪里调用?我们可以在 React 类组件中使用 Hooks 吗?关于 Hooks 的顺序,我们需要注意什么?可以在条件、循环或嵌套函数中调用 Hooks 吗?Hooks 的命名约定是什么?我们如何自动执行 Hooks 的规则?什么是穷举依赖关系规则?我们如何自动修复棉绒警告?延伸阅读如果您对我们在本章中学到的概念的更多信息感兴趣,请查看以下阅读材料:React 官方文档中的 Hooks 规则:https://reactjs.org/docs/hooks-rules.html。ESLint 官方网站:https://eslint.org/.
2021年04月08日
95 阅读
0 评论
1 点赞
1
2
...
7