2020-03-02
You clicked {this.state.count} times
You clicked {count} times
You clicked {this.state.count} times
You clicked {count} times
{this.state.firstName} {this.state.lastName}
Hello, {firstName} {lastName}
{this.state.firstName} {this.state.lastName}
Hello, {firstName} {lastName}
{props.data.team}
{context.team}
{context.team}
{context.team}
{context.team}
{context.team}
{context.team}
toggleComplete(todo.id)}>
{todo.name}
接下来,我们需要一个函数来处理这个点击事件。它很简单,只发送一个简单的id和操作类型。将此添加到addTodo()函数的正下方:
function toggleComplete(id) {
dispatch({ type: 'TOGGLE_COMPLETE', id });
}
最后,我们添加下面代码到todoReducer中:
case 'TOGGLE_COMPLETE': {
return state.map((item) =>
item.id === action.id
? { ...item, complete: !item.complete }
: item
)
}
我还设置了一个样式,我们将根据todo的完整值是否为true来添加或删除该样式。在todos.map代码下面,让我们更改如下所示的代码行:
改为:
我们不再需要alt属性,所以我们删除了它。现在,当我们单击todo时,它将分派一个操作,并将该特定todo的completed值设置为true,现在,我们的过滤器将通过useffect方法来获取这个值,该方法反过来更新document.title。我们还将添加类名completed,并且完成的todo将变得不透明,以表示完成的todo。
在这个时候,除了delete功能,以及清除列表中所有todo的按钮之外,我们几乎所有的东西都在起作用。为了完成我们的演示,我们将重复我们已经学到的使最后两个功能工作的内容。
删除一个Todo项
首先,为todos HTML中的close图标添加onClick()事件:
)
}
export default Counter;
因此,我们希望在这里创建一个自定义钩子,将一段文本传递到钩子中,钩子会为我们更新文档标题。首先让我们看看创建此自定义挂钩所需的代码:
const useDocumentTitle = (title) => {
useEffect(() => {
document.title = title;
}, [title])
}
在上面你可以看到我们需要这个钩子作为参数的是一个字符串,我们称之为title。在钩子中,我们调用React Core的基本useffect钩子,并设置标题。useffect的第二个参数将为我们执行该检查,并且仅当标题的本地状态与我们传入的不同时才更新标题。你的意思是,创建自定义钩子和创建函数一样简单?是的,它的核心非常简单,而且该函数可以引用任何其他钩子。该死的…创建自定义钩子比我们想象的要容易!
让我们回顾一下我们的整体功能组件现在的样子。你会看到我把useffect的旧调用注释掉了,上面是我们如何使用自定义钩子来代替它。
import React, { Component, useState, useEffect } from 'react';
const useDocumentTitle = title => {
useEffect(() => {
document.title = title;
}, [title])
}
function Counter() {
const [count, setCount] = useState(0);
const incrementCount = () => setCount(count + 1);
useDocumentTitle(`You clicked ${count} times`);
// useEffect(() => {
// document.title = `You clicked ${count} times`
// });
return (
)
}
export default Counter;
让我们进一步清理一下,看看如果这个钩子是由某个npm包提供的,而不是被复制粘贴在文件的顶部,我们可以如何使用它。
import React, { Component, useState } from 'react';
import useDocumentTitle from '@rehooks/document-title';
function Counter() {
const [count, setCount] = useState(0);
const incrementCount = () => setCount(count + 1);
useDocumentTitle(`You clicked ${count} times`);
return (
)
}
export default Counter;
这真是太棒了,但我也希望您注意到,我不必在我的功能组件中导入useffect,因为我们从“@rehooks/document title”导入的钩子会处理这个问题。所以如果我不需要useffect,我可以从组件导入中省略它。
我希望这说明了创建自定义React钩子的基本原理,并且您甚至可以通过这样一个简单的例子看到它的威力。
管理KendoReact组件的控制状态
Hooks非常适合处理特定类型的应用程序状态。例如控制状态、本地组件状态和会话状态。在使用KendoReact UI(https://www.telerik.com/kendo-react-ui/)组件时,我想利用Hooks,但我想从简单开始。我们将重构,不再使用类,而是使用函数组件。我们将查找演示使用this.state和this.setState的实例,因为当我们将组件转换为函数时,我们将不再需要使用this关键字,我们将不需要使用构造函数或调用setState。
因此,让我们开始重构KendoReact演示,演示如何使用我们的KendoReact对话框。
如果您看看下面演示的main.jsx(https://stackblitz.com/edit/kendoreact-dialog-class-based?file=app/main.jsx)页面,我们可以确定在使用功能组件和React钩子时会发生变化的几个目标区域。用绿色突出显示的代码和行需要修改,用红色突出显示的行可以完全删除。
image
在第6行有一个类定义,我们需要把它转换成函数组件
第7行有一个构造器,第8行调用了super()方法,第10行有一些绑定。在使用Hooks的函数组件中这些都是不需要的。
在第9行中,我们创建一个状态实例,并给它一个默认值true,这将是对useState钩子的调用。
在第13行,我们需要重命名toggleDialog函数并将其切换到ES6箭头函数样式语法,第14行到第16行只需调用useState()赋值提供的更新方法setVisible,它将引用的值将是可见的,而不是this.state.visible。
在第19行中,我们有一个render()调用,这在函数组件中是不必要的
在第22、23、26和27行我们提到了这一点。而this.state需要引用visible和toggleVisible而不是toggleDialog,稍后我将解释为什么要重命名该函数。
首先要做的就是将组件转为函数组件,移除constructor构造器,删除supr()引用以及toggleDialog()函数绑定。这里可以使用多种语法选项,我更喜欢ES6箭头函数样式:
const multiply = (x, y) => { return x * y };
在我们的组件中,第6行现在看起来如下:
const DialogWrapper = () => {
让我们来设置一个钩子来代替state对象。我们将不创建名为state的对象,而是设置对useState()的调用,并将其返回值解构为一个变量,该变量将保存我们的状态和更新/设置方法来更新该状态。我们的状态名称将是可见的,其更新方法将被称为setVisible。我们将删除整个构造函数并将其替换为这一行:
const [visible, setVisible] = useState(true);
因为我们使用的是useState()基本钩子,所以还需要导入它。我们的React导入现在看起来像:
import React, { useState } from 'react';
接下来,我们需要一个在这个组件中调用setVisible的函数来切换它的值。我们将其命名为toggleVisible,而不是toggleDialog,因为我们在一个功能组件中,所以之前使用的语法将不起作用。相反,我将更新为ES6箭头函数样式。此函数只需将可视状态设置为与当前状态相反的状态。
const DialogWrapper = () => {;
const [visible, setVisible] = useState(true);
const toggleVisible = () => setVisible(!visible);
现在我们需要去掉render()块及其两个大括号。此外,我们需要删除对this.toggleDialog和this.state.visible的所有引用,并相应地将它们更改为toggleVisible和visible。现在在return()中,我们将进行以下更改:
return (
deleteTodo(todo.id)}>
×
我们将添加操作函数来处理这些操作,它不必是它们自己的函数,我们可以直接从onClick()传递,或者我们可以设置一个类似的switch语句来处理所有分配。我们可以采取任何我们想要的方法。为了演示的目的,我想逐个添加它们。
现在我们创建一个函数来处理dispatch:
function deleteTodo(id) {
dispatch({ type: 'DELETE_TODO', id });
}
现在我们只需在reducer的switch语句中添加一个case来处理reduce。在这里,我们使用数组的.filter()方法从列表中删除一个满足id的todo项并返回状态。
case 'DELETE_TODO': {
return state.filter((x) => x.id !== action.id);
}
清除所有Todos
对于清除todo操作没什么特别的,我们只需要返回一个空数组。下面是实现这一点所需的三段不同的代码。
将onClick()添加到HTML按钮:
onClick={() => clearTodos()}
添加一个方法处理dispatch:
function clearTodos() {
dispatch({ type: 'CLEAR_TODOS' });
}
在我们的reducer方法里添加一个case:
case 'CLEAR_TODOS': {
return [];
}
Reducers总结
我们现在已经使用useReducer构建了Todo应用程序的基础。当处理数据子级别稍微复杂一点的状态时,此模式将非常有用。我们了解了纯函数以及为什么它们是reducer的核心,允许我们返回可预测的状态,现在使用这种模式在核心React库中更容易实现。
第五节:自定义React Hooks
让我们学习如何创建一个定制的React钩子,以及使用钩子时必须记住的所有规则。
Hooks只是功能!任何函数都可以成为Hooks。我觉得ReactJS文档站点上的文档不够简单。这不是敲打他们,我只是觉得,如果我能尝试用更简单的方式来解释,更多的人会受益。
重温 Effect Hook
如果您对基本Hooks有足够的了解,可以直接跳到创建自定义Hooks。不必再回顾所有的基本Hooks,我想我们只需要重新访问其中一个:useffect钩子。我在阅读ReactJS.org文档的Hooks时了解到,有两种方法可以使用useffect。无需清除的 effect和需要清除的effect。我希望在这个阶段使用Hooks的任何人要么知道这些术语,要么花几分钟来读一下官方文档。
在类和Hooks可用之前,effect被放在许多生命周期方法中,比如:componentDidMount 或者 componentDidUpdate。如果在这两种方法中都有重复的代码(执行相同的处理和更新效果),现在我们可以在功能组件中执行这些操作,只需一个钩子就可以完成。
useffect告诉React我们的组件需要在组件呈现之后做一些事情。它在第一次渲染之后和每次更新之后运行。在我之前的文章中,我只讨论了没有清理的副作用,所以我想很快地介绍如何允许功能组件在清理时产生副作用。
下面是一个示例,说明如何在不进行任何清理的情况下运行useffect:
useEffect(() => {
document.title = `You clicked ${count} times`;
});
如果确实需要清理才能运行,可以从useffect返回函数。这是可选的,它允许您在效果之后和任何新效果运行之前运行一些代码。订阅某些内容的情况可能需要取消订阅,作为效果清理过程的一部分。React将在卸载时执行此清理。
useEffect(() => {
console.log("Subscribe to Something);
return function cleanup() {
console.log("Unsubscribe to Something);
};
});
以上效果将在每个渲染上运行一次以上。React在运行下一个渲染的效果之前清除上一个渲染的效果,这应该注意。有关为什么在每次更新时都运行hook的解释,请查看ReactJS文档。不过,请记住,如果此行为导致性能问题,则可以选择退出。
我们还可以通过使用可选参数跳过效果来优化性能。例如,可能我们不想运行subscribe/unsubscribe效果,除非某些id已更改。看看下面的例子,了解如何做到这一点,这是相当简单的!
useEffect(() => {
console.log("Subscribe to Something);
return () => {
console.log("Unsubscribe to Something);
};
}, [props.something.id]); // only if something.id changes
Hooks,特别是useffect,现在允许您根据代码正在做什么而不是它在什么生命周期方法中拆分代码。当我们只有类和生命周期方法时,我们有时不得不混合关注点。现在,使用多个useffect方法,React可以按指定的顺序应用每个效果。这对于在应用程序中组织代码是一个巨大的好处。
创建自定义Hook
我真的很喜欢亚当·拉基斯(Adam Rackis)最近在推特上发表的一篇文章:“Hooks的创作水平远远超过我们所看到的任何东西。”关于Hooks,我想让你了解的是,我们在类中看到的所有伟大的变化,以及我们如何有这么多的组合选项,现在Hooks中都有了这些。这意味着,现在当涉及到React中功能组件的组成时,我们的手是不受约束的。对于React开发人员来说,这是一个巨大的进步。
自定义钩子就是JavaScript函数,其名称以单use作为前缀。自定义钩子是一个普通函数,但我们使用不同的标准。通过在开头添加use这个词,我们知道这个函数遵循Hooks的规则。
有了对Hook的更好理解,让我们把我们知道的作为一段简单的代码,我们的文档标题更新,并创建一个简单的自定义Hook。
似乎我们需要在几个页面上或在应用程序中的许多不同功能组件中执行某些操作。当信息更改时,我们希望用某种类型的字符串更新文档标题。另外,我们不想在每个功能组件中重复这个逻辑。我们将从在同一页面的本地将此代码提取到钩子开始,然后查看如何将同一钩子导入到多个组件并共同定位。很简单吧?
如果这是真的,那么我们的自定义钩子也可以调用React Core基本钩子之一,比如useffect。让我们再检查一次更新文档标题的功能组件。
import React, { Component, useState, useEffect } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const incrementCount = () => setCount(count + 1);
useEffect(() => {
document.title = `You clicked ${count} times`
});
return (
You clicked {count} times
You clicked {count} times
You clicked {count} times
{visible && }
);
同样,我们刚刚更新了return()中的代码,以不引用this关键字并使用新的函数名toggleVisible。
我们已经成功地将KendoReact演示转换为使用功能组件和基本useState挂钩。让我们使用一个叫做githrisk的很棒的工具来看看我们的整体变化是什么样子的:
image
总结
我希望本指南能帮助您更好地理解Hooks的基础知识,并允许您在这些示例的基础上创建新的和令人惊奇的东西。如果它对你有用,请分享和传播。
我想让你对Hooks的创建有一个很好的理解,我认为这可以通过回顾Sophie Alpert在React Conf 2018上的演讲得到最好的解释。
在过去,一些React开发人员在何时使用和何时不使用类方面遇到了困惑。这个问题可以追溯到几年前,在一个案例中,丹阿布拉莫夫的一篇文章中写道:如何使用React类在晚上睡觉。
尽管我们有时可能在当前的React中使用它们,或者在将来处理遗留代码时遇到它们,但这个问题现在正在处理中,我们已经看到开发人员有很强的见解,并且大多使用功能组件。
当谈到React团队正在做些什么,以便更容易地构建优秀的UI,并改进React中的开发人员体验时,Sophie Alpert提出了一个很好的问题。
为什么React仍然很糟糕?
以下是React Conf 2018大会上著名演讲的答案:
重用逻辑
在React hook之前,我们使用了很多高阶组件和渲染道具来实现这一点,这将需要您在使用这些模式时经常重新构建应用程序,并导致包装地狱(末日金字塔风格的嵌套)。
巨大的部件
由于在不同的生命周期方法中分割出不同的逻辑片段,我们的组件中经常出现混乱。
混淆类
这是我将留给你的许多引语中的第一个引语。
课程对人类来说很难,但不仅仅是人类,课程对机器来说也很难——索菲·阿尔伯特
理解JavaScript中的类可能很棘手,而且在hook之前,还需要使用类组件来访问状态和生命周期方法。简单地定义类组件需要相当多的样板文件。钩子有助于解决这些问题,出于这个原因,我想留给你一些其他值得注意的引用,我们的无畏反应和社区领袖!
Hooks允许您始终使用函数,而不是在函数、类、HOC和渲染道具之间切换——Dan Abramov
如果你想让世界变得更美好,看看React Hooks,然后做出改变。--迈克尔杰克逊
Hooks提供了一种处理React中问题的新方法——Dave Ceddia
有了React钩子,我们拥有了两个世界中最好的:可以使用状态的干净功能组件——David Katz
钩子是React,是React要去的地方,是React V2——迈克尔杰克逊
React钩子从根本上简化了我创建、编写、读取和原型组件的方式——Zach Johnson
这些就是人们所说的关于React Hooks的一些事情。
如果你希望了解更多前端知识,请关注我的公众号“前端记事本”

发表评论 (审核通过后显示评论):