【译】React Hooks的5个重要原则

  • 1983字
  • 10分钟
  • 2024-07-27

在当下,React无疑仍然是创建前端应用程序的最受欢迎选择之一。这并不是因为它没有缺点,而是因为 React 多年来积累了庞大的社区和巨大的人气。

很难想象没有钩子的 React,但不幸的是,我们经常看到开发人员过度使用它们。结果,他们不得不解决依赖数组、无用的重新渲染以及它们造成的所有混乱问题,因为他们内心深处担心,在 React 中,只有使用钩子才能完成所有事情。

在本文中,我将讨论每个新 React 程序员应该知道的五个原则,以改进和简化他们的代码。

1. 不是每个函数都必须是一个Hook

让我们从基础开始,先看看定义

Hook 使用 JavaScript 函数定义,但它们代表一种特殊的可重用 UI 逻辑,并且它们在调用位置上有限制。当你使用 Hook 时,需要遵循 Hook 的规则。

Hooks看起来像函数,但有一些区别:

  • Hooks只能在函数组件或自定义Hooks中使用。
  • Hook的名称总是以“use”开头,后跟一个大写字母。
  • 如果自定义Hook内部不包含对其他Hooks的调用,它就是一个函数,而不是一个Hook。这很重要,因为它有助于确定是否有一些状态逻辑或副作用在其中。

让我们创建一个名为useBoolean的简单自定义Hook,以满足这些要求。我们可以用它来打开/关闭面板、对话框、显示/隐藏元素等。

如果查看官方文档,您会发现建议将Hook返回的任何函数包装在useCallback中,我们也将这样做。

1
interface ICallbacks {
2
setFalse: () => void;
3
setTrue: () => void;
4
toggle: () => void;
5
}
6
7
const useBoolean = (initialValue: boolean): [boolean, ICallbacks] => {
8
const [value, setValue] = useState(initialValue);
9
10
const setFalse = useCallback(() => {
11
setValue(false);
12
}, []);
13
14
const setTrue = useCallback(() => {
15
setValue(true);
16
}, []);
17
18
const toggle = useCallback(() => {
19
setValue((curValue) => !curValue);
20
}, []);
21
22
return [value, { setFalse, setTrue, toggle }];
23
};

记住基础知识后,我们可以更深入地了解一些细微差别。

2. 了解重新渲染

你可能会问为什么。

理解React如何工作以及当您通过setter函数更改组件的状态时会发生什么,这一点很重要。乍一看,您似乎改变了状态,结果必须立即出现,但事实是这样的吗?

当你知道改变状态发生了什么时,就更容易理解为什么以及何时useEffect或其他带依赖数组的Hooks会被触发。

让我们看看一个简单的例子。想象一下,我们第一次按下按钮并调用onChangeText,传递值“newValue”。

1
const [text, setText] = useState("defaultValue");
2
3
const onChangeText = (value: string) => {
4
// value 等于 "newValue"
5
setText(value);
6
7
console.log(text); // 这里的值会是什么?
8
};

看起来我们应该在控制台中看到“newValue”,但实际上会是“defaultValue”。为什么?因为新值只有在重新渲染后才可用。

有必要看到发生的步骤:

  1. 我们通过setter函数改变状态,告诉React采取行动。
  2. 渲染。React调用你的组件以计算新的JSX,这将被返回。
  3. 提交。计算完变化后,React将修改DOM;最小的动作将被应用。
  4. 在前面的步骤之后,您将在屏幕上看到视觉变化(“浏览器渲染”)。

每次你想改变状态中的值时,都需要记住这些步骤每次都会完成。

3. useState并不总是正确的答案

在React中,我们有两种管理组件状态的方法——useState和useReducer。第二种方法不太流行,因为它适用于状态中更复杂的对象,老实说,对于新程序员来说,乍一看它看起来太复杂了,但事实并非如此。

然而,useState看起来非常简单易懂,所以新程序员往往使用它的次数超过了实际需要。

它旨在根据用户交互来管理重新绘制组件的状态。如果你想记住一些内容而不进行渲染,可能不应该将其放在状态中。useRef会是更好的选择。

如果:

  • 你想在重新渲染期间记住一些值而不向用户展示它们。
  • 你已经在状态中有数据,或者你通过props接收它,但需要转换它;你不需要将新值保存在新的useState对象中,创建一个新变量并操作它而不会触发无用的重新渲染。

你需要将值保存在状态中,如果:

  • 你想在值改变时重新绘制组件;最常见的例子是显示/隐藏面板、旋转器、错误消息以及修改数组。

将你的代码简化如下:

1
/**
2
以下代码导致无用的重新渲染和不必要的useEffect使用。
3
当name或description变化是,React会重新渲染组件
4
**/
5
6
const [name, setName] = useState("name");
7
const { description, index } = props;
8
const [fullName, setFullName] = useState("");
9
10
useEffect(() => {
11
setFullName(`${name} - ${description}`);
12
}, [name, description]);

更改为:

1
/**
2
我们可以使用React的默认行为,在name
3
或description更改时获取正确的值,
4
而不触发一次更多的重新渲染。
5
**/
6
const [name, setName] = useState();
7
const { description, index } = props;
8
const nameWithDescription = `${name} - ${description}`;

4. 使用useEffect时需要非常小心

useEffect是一个React Hook,它让你可以同步一个组件和外部系统。

让我们看看官方文档

但实际上,我们使用useEffect的次数远超实际需要。它非常适合在组件挂载时获取数据,但不幸的是,新程序员倾向于使用这个Hook来改变状态,这不是最好的解决方案。

如果发现自己在一个组件内部写一个又一个useEffect,请停下来审查代码。通常你不需要它们,而且你可以很快消除它们。

如果符合以下情况则不需要 useEffect:

  • 你需要处理用户事件(click);如果你知道哪些动作会触发某些逻辑,不要使用useEffect来调用该逻辑。
  • 你需要转换数据以进行渲染,例如从状态或props中连接字符串。将响应性值或逻辑放入依赖数组中可能会导致useEffect被过于频繁地调用,导致无限循环。

如果出现以下情况,您需要选择 useEffect:

  • 你想在组件挂载时获取数据,设置间隔,并用它来同步状态与其他系统

5. 不要害怕useRef

不幸的是,useRef被低估了。它不是最流行的Hooks之一,但它非常有用。知道如何以及在哪里使用它可以取得很好的效果。

让我们从基础开始。

useRef是一个React Hook,它让你可以引用不需要渲染的值。——来自React官方文档。

无论你是在创建一个引用DOM中的节点的JavaScript对象还是一个简单的值,React都会记住你通过useRef创建的值,并且它不会在重新渲染期间丢失。

它给我们带来了什么?

  • 我们可以轻松访问DOM中的元素。例如,你可以获取输入字段的值,聚焦到特定元素,获取它们的高度和宽度,滚动到屏幕的特定部分等。
  • 你可以记住任何你需要的数据而不重新渲染组件。例如,如果你需要一个计数器或计时器,选择useRef而不是useState。

例子:

1
// 引用一个数字
2
const refCount= useRef(0);
3
// 引用一个输入元素
4
const refInputField = useRef(null);
5
6
/**
7
要访问该值,你需要使用current属性。
8
useRef不会触发重新渲染,所以你可以在useEffect内部使用它
9
而不用担心依赖数组
10
*/
11
12
const onClick = () => {
13
refCount.current = refCount.current + 1;
14
refInputField.current.focus();
15
}
16
17
return (
18
<>
19
<button onClick={onClick}>
20
Click me!
21
</button>
22
<input ref={refInputField}></input>
23
</>
24
);
美团外卖红包 饿了么红包 支付宝红包