在本文结束时,您将能够回答以下问题:
- 什么是 hooks?
- 如何使用 hooks?
- 使用 hooks 的一些规则?
- 什么是 custom hook(自定义钩子)?
- 什么时候应该使用 custom hooks?
- 使用 custom hooks 有什么好处?
Hooks 可以做到以下事情:
- 在功能组件中使用 state 和“hook into”的生命周期方法。
- 在组件之间重用有状态逻辑,这简化了组件逻辑,最重要的是,让你跳过编写 classes。
如果你已经使用过 React,你就会知道复杂多变,有状态的逻辑是如何得到的,当应用程序为功能添加了几个新功能时,就会发生组件代码变得复杂而难以维护这种情况。为了尝试简化这个问题,React 背后的大脑试图找到解决这个问题的方法。
(1)在组件之间重用有状态逻辑
hooks 允许开发人员编写简单,有状态的功能组件,并在开发时花费更少的时间来设计和重构组件层次结构。怎么样?使用钩子,您可以在组件之间_获取_和_分享_有状态逻辑。
(2)简化组件逻辑
当您的应用程序中出现不可避免的指数级逻辑增长时,简单的组件就回因为各种状态逻辑和生命周期等等因素,而变得繁琐和复杂。组件的职责增长并变得不可分割。反过来,这使编码变得麻烦并且测试困难。
class 是 React 架构的重要组成部分。class 有许多好处,但它们为初学者创造了入门的障碍。对于 class,您还必须记住将 this 绑定到事件处理程序,因此代码可能变得冗长且有点多余。
React 版本 16.8.
import {useState, useEffect} from 'react';
很简单,但你如何实际使用这些新方法?以下示例非常简单,但这些方法的功能非常强大。
使用状态钩子(state hook)的最好方法是对其进行解构并设置原始值。第一个参数将用于存储状态,第二个参数用于更新状态。
例如:
const [weight, setWeight] = useState(150);
onClick={() => setWeight(weight + 15)}
weight 是状态
setWeight 是一种用于更新状态的方法
useState(150)是用于设置初始值(任何基本类型)的方法
值得注意的是,您可以在单个组件中多次构造状态 hook:
const [age, setAge] = useState(42);
const [month, setMonth] = useState('February');
const [todos, setTodos] = useState([{text: 'Eat pie'}]);
因此,该组件可能看起来像:
import React, {useState} from 'react';
export default function App() {const [weight, setWeight] = useState(150);
const [age] = useState(42);
const [month] = useState('February');
const [todos] = useState([{text: 'Eat pie'}]);
return (Current Weight: {weight}
Age: {age}
Month: {month}
setWeight(weight + 15)}>
{todos[0].text}
);
}
使用 effect hook 就好像使用 componentDidMount, componentDidUpdate, 和 componentWillUnmount 这类的生命周期的方法。
例如:
// similar to the componentDidMount and componentDidUpdate methods
useEffect(() => {document.title = You clicked ${count} times;
});
组件更新的任何时候,渲染后都会调用 useEffect。现在,如果你只想在变量 count 改变时更新 useEffect,你只需将该事实添加到数组中方法的末尾,类似于高阶 reduce 方法末尾的累加器。
// check out the variable count in the array at the end...
useEffect(() => {document.title = You clicked ${count} times;
}, [count]);
让我们结合两个例子:
const [weight, setWeight] = useState(150);
useEffect(() => {document.title = You weigh ${weight}, you ok with that?;
}, [weight]);
onClick={() => setWeight(weight + 15)}
因此,当触发 onClick 时,也会调用 useEffect 方法,并在 DOM 更新后在文档标题中呈现新的数据。
例:
import React, {useState, useEffect} from 'react';
export default function App() {const [weight, setWeight] = useState(150);
const [age] = useState(42);
const [month] = useState('February');
const [todos] = useState([{text: 'Eat pie'}]);
useEffect(() => {document.title = You weigh ${weight}, you ok with that?;
});
return (Current Weight: {weight}
Age: {age}
Month: {month}
setWeight(weight + 15)}>
{todos[0].text}
);
}
useEffect 非常适合进行 API 调用:
useEffect(() => {fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(results => results.json())
.then((data) => {setTodos([{ text: data.title}]);
});
}, []);
React 钩子看起来很棒,但是如果你花一点时间,你可能会意识到在多个组件中重新初始化多个钩子方法,比如 useState 和 useEffect,可能会违背 DRY(Don’t repeat yourself)原则。那么,让我们看看如何通过创建自定义钩子来重用这些新内置方法。
是的,React 钩子有规则。这些规则乍一看似乎是非常规的,但是一旦你理解了 React 钩子如何启动的基础知识,规则就很容易理解。
(1) 必须在顶层以相同的顺序调用挂钩。(依次调用)
Hooks 创建一个钩子调用数组来保持秩序。这个命令有助于 React 告诉区别,例如,在单个组件中或跨应用程序的多个 useState 和 useEffect 方法调用之间。
例如:
// This is good!
function ComponentWithHooks() {
// top-level!
const [age, setAge] = useState(42);
const [month, setMonth] = useState('February');
const [todos, setTodos] = useState([{text: 'Eat pie'}]);
return (//...)
}
在第一次渲染时,42,February,[{text:’Eat pie’}]都被推入状态数组。
当组件重新渲染时,忽略 useState 方法参数。
age,month 和 todos 的值是从组件的状态中检索的,这是前面提到的状态数组。
(2) 无法在 条件语句或循环中 调用挂钩。
由于启动 hooks 的方式,不允许使用 within 条件语句或循环。对于 hooks,如果在重新渲染期间初始化的顺序发生变化,则很可能您的应用程序无法正常运行。您仍然可以在组件中使用条件语句和循环,但不能在代码块内使用钩子。
例如:
// DON'T DO THIS!!
const [DNAMatch, setDNAMatch] = useState(false)
if (name) {setDNAMatch(true)
const [name, setName] = useState(name)
useEffect(function persistFamily() {localStorage.setItem('dad', name);
}, []);
}
// DO THIS!!
const [DNAMatch, setDNAMatch] = useState(false)
const [name, setName] = useState(null)
useEffect(() => {if (name) {setDNAMatch(true)
setName(name)
localStorage.setItem('dad', name);
}
}, []);
(3) 钩子不能用在 class 组件中。
钩子必须在功能组件或自定义钩子函数中初始化。自定义钩子函数只能在功能组件中调用,并且必须遵循与非自定义钩子相同的规则。
您仍然可以在同一个应用程序中使用类组件。您可以使用 hooks 作为类组件的子项呈现功能组件。
(4) 自定义钩子应该以 单词 use 开头并且是驼峰式 的。
这是一个强有力的建议而非规则,但它将有助于您的应用程序的一致性。你也会知道,当你看到一个以“use”为前缀的函数时,它可能是一个自定义钩子。
自定义挂钩只是遵循与非自定义挂钩相同规则的函数。它们允许您整合逻辑,共享数据以及跨组件重用钩子。
当您需要在组件之间共享逻辑时,最好使用自定义挂钩。在 JavaScript 中,当您想要在两个单独的函数之间共享逻辑时,您可以创建另一个函数来支持它。好吧,就像组件一样,hooks 也是 function。您可以提取 hooks 逻辑,以便在应用程序的组件之间共享。在编写自定义 hooks 时,您可以命名它们(再次以“use”开头),设置参数,并告诉它们应该返回什么(如果有的话)。
例如:
import {useEffect, useState} from 'react';
const useFetch = ({url, defaultData = null}) => {const [data, setData] = useState(defaultData);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {fetch(url)
.then(res => res.json())
.then((res) => {setData(res);
setLoading(false);
})
.catch((err) => {setError(err);
setLoading(false);
});
}, []);
const fetchResults = {
data,
loading,
error,
};
return fetchResults;
};
export default useFetch;
Hooks 允许您在应用程序增长时抑制复杂性,并编写更易于理解的代码。下面的代码是两个具有相同功能的组件的比较。在第一次比较之后,我们将在伴随容器的组件中使用自定义钩子展示更多好处。
以下类组件应该看起来:
import React from 'react';
class OneChanceButton extends React.Component {constructor(props) {super(props);
this.state = {clicked: false,};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {return this.setState({ clicked: true});
}
render() {
return (You Have One Chance to Click);
}
}
export default OneChanceButton;
如何使用钩子实现相同的功能来简化代码并提高可读性:
import React, {useState} from 'react';
function OneChanceButton(props) {const [clicked, setClicked] = useState(false);
function doClick() {return setClicked(true);
}
return (You Have One Chance to Click);
}
export default OneChanceButton;
React hooks 是一个惊人的新功能!实施的理由是合理的; 并且,再加上这一点,我相信这将极大地降低 React 编码的门槛,并使其保持在最喜欢的框架之上列表。看到这会如何改变第三方库的工作方式,尤其是状态管理工具和路由器,将会非常令人兴奋。
总之,React 钩子:
- 无需使用类组件即可轻松与 React 的生命周期方法相关联
- 通过增加可重用性和抽象复杂性来帮助减少代码量
- 帮助简化组件之间共享数据的方式
推荐阅读:https://juejin.im/post/5be8d3…