hooks-pattern
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHooks Pattern
Hooks模式
React 16.8 introduced a new feature called Hooks. Hooks make it possible to use React state and lifecycle methods, without having to use an ES2015 class component.
Although Hooks are not necessarily a design pattern, Hooks play a very important role in your application design. Many traditional design patterns can be replaced by Hooks.
React 16.8 引入了一项名为Hooks的新特性。借助Hooks,无需使用ES2015类组件即可使用React状态和生命周期方法。
尽管Hooks不一定属于设计模式,但它在应用设计中扮演着非常重要的角色。许多传统设计模式都可以被Hooks替代。
When to Use
适用场景
- Use this when you need to add state or lifecycle behavior to functional components
- This is helpful for extracting and reusing stateful logic across multiple components
- Use this instead of class components for cleaner, more composable code
- 当你需要为函数式组件添加状态或生命周期行为时使用
- 有助于提取并在多个组件之间复用有状态逻辑
- 替代类组件,编写更简洁、更具组合性的代码
Instructions
使用说明
- Use for local state and
useStatefor side effects in functional componentsuseEffect - Create custom hooks (prefixed with ) to encapsulate and share reusable logic
use - Follow the Rules of Hooks: only call hooks at the top level and only in React functions
- Avoid unnecessary — compute derived state directly in the component body
useEffect - Let the React Compiler handle memoization instead of manual /
useMemowhere possibleuseCallback
- 在函数式组件中使用管理本地状态,使用
useState处理副作用useEffect - 创建自定义Hooks(以为前缀)来封装和共享可复用逻辑
use - 遵循Hooks规则:仅在React函数的顶层调用Hooks
- 避免不必要的——直接在组件主体中计算派生状态
useEffect - 尽可能让React编译器处理记忆化,而非手动使用/
useMemouseCallback
Details
详细内容
Class components
类组件
Before Hooks were introduced in React, we had to use class components in order to add state and lifecycle methods to components. A typical class component in React can look something like:
js
class MyComponent extends React.Component {
/* Adding state and binding custom methods */
constructor() {
super()
this.state = { ... }
this.customMethodOne = this.customMethodOne.bind(this)
this.customMethodTwo = this.customMethodTwo.bind(this)
}
/* Lifecycle Methods */
componentDidMount() { ...}
componentWillUnmount() { ... }
/* Custom methods */
customMethodOne() { ... }
customMethodTwo() { ... }
render() { return { ... }}
}A class component can contain a state in its constructor, lifecycle methods such as and to perform side effects based on a component's lifecycle, and custom methods to add extra logic to a class.
componentDidMountcomponentWillUnmountAlthough we can still use class components after the introduction of React Hooks, using class components can have some downsides! Let's look at some of the most common issues when using class components.
在React引入Hooks之前,我们必须使用类组件才能为组件添加状态和生命周期方法。一个典型的React类组件如下所示:
js
class MyComponent extends React.Component {
/* 添加状态并绑定自定义方法 */
constructor() {
super()
this.state = { ... }
this.customMethodOne = this.customMethodOne.bind(this)
this.customMethodTwo = this.customMethodTwo.bind(this)
}
/* 生命周期方法 */
componentDidMount() { ...}
componentWillUnmount() { ... }
/* 自定义方法 */
customMethodOne() { ... }
customMethodTwo() { ... }
render() { return { ... }}
}类组件可以在构造函数中包含状态,通过和等生命周期方法根据组件生命周期执行副作用,还可以添加自定义方法来扩展类的逻辑。
componentDidMountcomponentWillUnmount尽管在React Hooks引入后我们仍可使用类组件,但类组件存在一些弊端!让我们看看使用类组件时最常见的几个问题。
Understanding ES2015 classes
理解ES2015类
Since class components were the only component that could handle state and lifecycle methods before React Hooks, we often ended up having to refactor functional components into a class components, in order to add the extra functionality.
In this example, we have a simple that functions as a button.
divjs
function Button() {
return <div className="btn">disabled</div>;
}Instead of always displaying , we want to change it to when the user clicks on the button, and add some extra CSS styling to the button when that happens.
disabledenabledIn order to do that, we need to add state to the component in order to know whether the status is or . This means that we'd have to refactor the functional component entirely, and make it a class component that keeps track of the button's state.
enableddisabledjs
export default class Button extends React.Component {
constructor() {
super();
this.state = { enabled: false };
}
render() {
const { enabled } = this.state;
const btnText = enabled ? "enabled" : "disabled";
return (
<div
className={`btn enabled-${enabled}`}
onClick={() => this.setState({ enabled: !enabled })}
>
{btnText}
</div>
);
}
}In this example, the component is very small and refactoring wasn't a such a great deal. However, your real-life components probably contain of many more lines of code, which makes refactoring the component a lot more difficult.
Besides having to make sure you don't accidentally change any behavior while refactoring the component, you also need to understand how ES2015 classes work. Why do we have to the custom methods? What does the do? Where does the keyword come from? It can be difficult to know how to refactor a component properly without accidentally changing the data flow.
bindconstructorthis由于在React Hooks出现之前,类组件是唯一能处理状态和生命周期方法的组件类型,我们常常需要将函数式组件重构为类组件,以添加额外功能。
举个例子,我们有一个简单的作为按钮。
divjs
function Button() {
return <div className="btn">disabled</div>;
}我们希望用户点击按钮时,将文本从改为,并为此添加一些额外的CSS样式。
disabledenabled要实现这一点,我们需要为组件添加状态,以跟踪按钮是处于还是状态。这意味着我们必须完全重构函数式组件,将其转换为跟踪按钮状态的类组件。
enableddisabledjs
export default class Button extends React.Component {
constructor() {
super();
this.state = { enabled: false };
}
render() {
const { enabled } = this.state;
const btnText = enabled ? "enabled" : "disabled";
return (
<div
className={`btn enabled-${enabled}`}
onClick={() => this.setState({ enabled: !enabled })}
>
{btnText}
</div>
);
}
}在这个例子中,组件很小,重构起来不算麻烦。但实际项目中的组件可能包含更多代码,这会让重构工作变得困难得多。
除了要确保重构时不会意外改变任何行为外,你还需要理解ES2015类的工作原理。为什么我们必须绑定自定义方法?构造函数的作用是什么?关键字来自哪里?如果不了解这些,就很难正确重构组件而不意外改变数据流。
thisRestructuring
重构复杂度
The common way to share code among several components, is by using the Higher Order Component or Render Props pattern. Although both patterns are valid and a good practice, adding those patterns at a later point in time requires you to restructure your application.
Besides having to restructure your app, which is trickier the bigger your components are, having many wrapping components in order to share code among deeper nested components can lead to something that's best referred to as a wrapper hell. It's not uncommon to open your dev tools and seeing a structure similar to:
js
<WrapperOne>
<WrapperTwo>
<WrapperThree>
<WrapperFour>
<WrapperFive>
<Component>
<h1>Finally in the component!</h1>
</Component>
</WrapperFive>
</WrapperFour>
</WrapperThree>
</WrapperTwo>
</WrapperOne>The wrapper hell can make it difficult to understand how data is flowing through your application, which can make it harder to figure out why unexpected behavior is happening.
在多个组件之间共享代码的常见方式是使用高阶组件(HOC)或渲染属性(Render Props)模式。尽管这两种模式都是有效的最佳实践,但在后期引入这些模式时需要重构应用。
除了重构应用(组件越大,重构越棘手),为了在深层嵌套组件之间共享代码而添加的大量包装组件,可能会导致所谓的**“包装地狱”**。打开开发者工具时,你可能会看到类似这样的结构:
js
<WrapperOne>
<WrapperTwo>
<WrapperThree>
<WrapperFour>
<WrapperFive>
<Component>
<h1>终于到目标组件了!</h1>
</Component>
</WrapperFive>
</WrapperFour>
</WrapperThree>
</WrapperTwo>
</WrapperOne>“包装地狱”会让你难以理解应用中的数据流,从而更难排查意外行为的原因。
Complexity
逻辑复杂度
As we add more logic to class components, the size of the component increases fast. Logic within that component can get tangled and unstructured, which can make it difficult for developers to understand where certain logic is used in the class component. This can make debugging and optimizing performance more difficult.
Lifecycle methods also require quite a lot of duplication in the code.
Although a component may be small, the logic within the component can already be quite tangled. Whereas certain parts are specific for the logic, other parts are specific for the logic. As your component grows, it can get increasingly difficult to structure logic within your component, find related logic within the component.
counterwidthBesides tangled logic, we're also duplicating some logic within the lifecycle methods. In both and , we're customizing the behavior of the app based on the window's event.
componentDidMountcomponentWillUnmountresize随着我们为类组件添加更多逻辑,组件的体积会迅速增大。组件内的逻辑会变得混乱且结构不清晰,这会让开发者难以理解类组件中特定逻辑的使用位置,从而增加调试和性能优化的难度。
生命周期方法还会导致大量代码重复。
即使组件很小,其内部的逻辑也可能已经相当混乱。某些部分属于逻辑,其他部分属于逻辑。随着组件的增长,你会越来越难以组织组件内的逻辑,也难以在组件中找到相关逻辑。
counterwidth除了逻辑混乱,我们还在生命周期方法中重复了一些逻辑。在和中,我们都根据窗口的事件自定义应用行为。
componentDidMountcomponentWillUnmountresizeHooks
Hooks
It's quite clear that class components aren't always a great feature in React. In order to solve the common issues that React developers can run into when using class components, React introduced React Hooks. React Hooks are functions that you can use to manage a components state and lifecycle methods. React Hooks make it possible to:
- add state to a functional component
- manage a component's lifecycle without having to use lifecycle methods such as and
componentDidMountcomponentWillUnmount - reuse the same stateful logic among multiple components throughout the app
First, let's take a look at how we can add state to a functional component, using React Hooks.
显然,类组件并非React中总是好用的特性。为了解决React开发者使用类组件时遇到的常见问题,React引入了React Hooks。React Hooks是一些函数,可用于管理组件的状态和生命周期方法。借助React Hooks,你可以:
- 为函数式组件添加状态
- 无需使用和
componentDidMount等生命周期方法即可管理组件的生命周期componentWillUnmount - 在应用的多个组件之间复用相同的有状态逻辑
首先,让我们看看如何使用React Hooks为函数式组件添加状态。
State Hook
状态Hook
React provides a hook that manages state within a functional component, called .
useStateLet's see how a class component can be restructured into a functional component, using the hook. We have a class component called , which simply renders an input field. The value of in the state updates, whenever the user types anything in the input field.
useStateInputinputjs
class Input extends React.Component {
constructor() {
super();
this.state = { input: "" };
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
this.setState({ input: e.target.value });
}
render() {
<input onChange={handleInput} value={this.state.input} />;
}
}In order to use the hook, we need to access the method that React provides for us. The method expects an argument: this is the initial value of the state, an empty string in this case.
useStateuseStateuseStateWe can destructure two values from the method:
useState- The current value of the state.
- The method with which we can update the state.
js
const [value, setValue] = React.useState(initialValue);The first value can be compared to a class component's .
The second value can be compared to a class component's method.
this.state.[value]this.setStateSince we're dealing with the value of an input, let's call the current value of the state , and the method in order to update the state . The initial value should be an empty string.
inputsetInputjs
const [input, setInput] = React.useState("");We can now refactor the class component into a stateful functional component.
Inputjs
function Input() {
const [input, setInput] = React.useState("");
return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}The value of the field is equal to the current value of the state, just like in the class component example. When the user types in the input field, the value of the state updates accordingly, using the method.
inputinputinputsetInputReact提供了一个用于管理函数式组件状态的Hook,名为。
useState让我们看看如何使用 Hook将类组件重构为函数式组件。我们有一个名为的类组件,它仅渲染一个输入框。当用户在输入框中输入内容时,状态中的值会更新。
useStateInputinputjs
class Input extends React.Component {
constructor() {
super();
this.state = { input: "" };
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
this.setState({ input: e.target.value });
}
render() {
<input onChange={handleInput} value={this.state.input} />;
}
}要使用 Hook,我们需要调用React提供的方法。方法接受一个参数:状态的初始值,这里是空字符串。
useStateuseStateuseState我们可以从方法中解构出两个值:
useState- 状态的当前值。
- 用于更新状态的方法。
js
const [value, setValue] = React.useState(initialValue);第一个值相当于类组件中的。
第二个值相当于类组件中的方法。
this.state.[value]this.setState由于我们处理的是输入框的值,我们将状态的当前值命名为,将更新状态的方法命名为。初始值为空字符串。
inputsetInputjs
const [input, setInput] = React.useState("");现在我们可以将类组件重构为有状态的函数式组件。
Inputjs
function Input() {
const [input, setInput] = React.useState("");
return <input onChange={(e) => setInput(e.target.value)} value={input} />;
}输入框的值等于状态的当前值,与类组件示例中的情况相同。当用户在输入框中输入内容时,状态的值会通过方法相应更新。
inputinputsetInputEffect Hook
副作用Hook
We've seen we can use the component to handle state within a functional component, but another benefit of class components was the possibility to add lifecycle methods to a component.
useStateWith the hook, we can "hook into" a components lifecycle. The hook effectively combines the , , and lifecycle methods.
useEffectuseEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmountjs
componentDidMount() { ... }
useEffect(() => { ... }, [])
componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])
componentDidUpdate() { ... }
useEffect(() => { ... })Let's use the input example we used in the State Hook section. Whenever the user is typing anything in the input field, we also want to log that value to the console.
We need to use a hook that "listens" to the value. We can do so, by adding to the dependency array of the hook. The dependency array is the second argument that the hook receives.
useEffectinputinputuseEffectuseEffectjs
useEffect(() => {
console.log(`The user typed ${input}`);
}, [input]);The value of the input now gets logged to the console whenever the user types a value.
我们已经了解到可以使用组件处理函数式组件内的状态,但类组件的另一个优势是可以为组件添加生命周期方法。
useState借助 Hook,我们可以“钩入”组件的生命周期。 Hook实际上结合了、和这三个生命周期方法。
useEffectuseEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmountjs
componentDidMount() { ... }
useEffect(() => { ... }, [])
componentWillUnmount() { ... }
useEffect(() => { return () => { ... } }, [])
componentDidUpdate() { ... }
useEffect(() => { ... })让我们使用状态Hook部分的输入框示例。每当用户在输入框中输入内容时,我们还希望将该值记录到控制台。
我们需要使用一个“监听”值的 Hook。我们可以通过将添加到 Hook的依赖数组中来实现这一点。依赖数组是 Hook接收的第二个参数。
inputuseEffectinputuseEffectuseEffectjs
useEffect(() => {
console.log(`The user typed ${input}`);
}, [input]);现在,每当用户输入内容时,输入框的值都会被记录到控制台。
Custom Hooks
自定义Hooks
Besides the built-in hooks that React provides (, , , , , , , , , ), we can easily create our own custom hooks.
useStateuseEffectuseReduceruseRefuseContextuseMemouseImperativeHandleuseLayoutEffectuseDebugValueuseCallbackYou may have noticed that all hooks start with . It's important to start your hooks with in order for React to check if it violates the Rules of Hooks.
useuseLet's say we want to keep track of certain keys the user may press when writing the input. Our custom hook should be able to receive the key we want to target as its argument.
js
function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = React.useState(false);
function handleDown({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
function handleUp({ key }) {
if (key === targetKey) {
setKeyPressed(false);
}
}
React.useEffect(() => {
window.addEventListener("keydown", handleDown);
window.addEventListener("keyup", handleUp);
return () => {
window.removeEventListener("keydown", handleDown);
window.removeEventListener("keyup", handleUp);
};
}, []);
return keyPressed;
}Instead of keeping the key press logic local to the component, we can now reuse the hook throughout multiple components, without having to rewrite the same logic over and over.
InputuseKeyPressAnother great advantage of Hooks, is that the community can build and share hooks. Here are some websites that list all the hooks built by the community, and ready to use in your application:
By rewriting the counter and width example using React Hooks, we can break the logic of the function into several pieces:
App- : A custom hook that returns the current value of
useCounter, ancountmethod, and aincrementmethod.decrement - : A custom hook that returns the window's current width.
useWindowWidth - : A functional, stateful component that returns the
AppandCountercomponent.Width
By using React Hooks instead of a class component, we were able to break the logic down into smaller, reusable pieces that separated the logic.
Using React Hooks just made it much clearer to separate the logic of our component into several smaller pieces. Reusing the same stateful logic just became much easier, and we no longer have to rewrite functional components into class components if we want to make the component stateful. A good knowledge of ES2015 classes is no longer required, and having reusable stateful logic increases the testability, flexibility and readability of components.
除了React提供的内置Hook(、、、、、、、、、),我们还可以轻松创建自己的自定义Hook。
useStateuseEffectuseReduceruseRefuseContextuseMemouseImperativeHandleuseLayoutEffectuseDebugValueuseCallback假设我们要跟踪用户在输入时按下的特定按键。我们的自定义Hook应该能够接收我们要监听的按键作为参数。
js
function useKeyPress(targetKey) {
const [keyPressed, setKeyPressed] = React.useState(false);
function handleDown({ key }) {
if (key === targetKey) {
setKeyPressed(true);
}
}
function handleUp({ key }) {
if (key === targetKey) {
setKeyPressed(false);
}
}
React.useEffect(() => {
window.addEventListener("keydown", handleDown);
window.addEventListener("keyup", handleUp);
return () => {
window.removeEventListener("keydown", handleDown);
window.removeEventListener("keyup", handleUp);
};
}, []);
return keyPressed;
}现在,我们可以在多个组件中复用 Hook,而无需重复编写相同的逻辑,而不是将按键监听逻辑局限在组件中。
useKeyPressInputHooks的另一个巨大优势是社区可以构建和共享Hook。以下是一些列出了社区构建的、可直接在应用中使用的Hook的网站:
通过使用React Hooks重写计数器和窗口宽度示例,我们可以将函数的逻辑拆分为几个部分:
App- :一个自定义Hook,返回
useCounter的当前值、count方法和increment方法。decrement - :一个自定义Hook,返回窗口的当前宽度。
useWindowWidth - :一个有状态的函数式组件,返回
App和Counter组件。Width
通过使用React Hooks而非类组件,我们能够将逻辑拆分为更小的、可复用的部分,实现逻辑分离。
使用React Hooks让我们能够更清晰地分离组件的逻辑,将其拆分为几个更小的部分。复用相同的有状态逻辑变得更加容易,而且如果我们想让组件变为有状态,也无需将函数式组件重写为类组件。不再需要掌握ES2015类的相关知识,复用有状态逻辑还能提高组件的可测试性、灵活性和可读性。
Additional Hooks guidance
额外的Hooks指南
Like other components, there are special functions that are used when you want to add Hooks to the code you have written. Here's a brief overview of some common Hook functions:
与其他组件一样,在现有代码中添加Hooks时需要使用一些特殊函数。以下是一些常见Hook函数的简要概述:
useState
useStateuseState
useStateThe Hook enables developers to update and manipulate state inside function components without needing to convert it to a class component. One advantage of this Hook is that it is simple and does not require as much complexity as other React Hooks.
useStateuseStateuseEffect
useEffectuseEffect
useEffectThe Hook is used to run code during major lifecycle events in a function component. The main body of a function component does not allow mutations, subscriptions, timers, logging, and other side effects. If they are allowed, it could lead to confusing bugs and inconsistencies within the UI. The useEffect hook prevents all of these "side effects" and allows the UI to run smoothly. It is a combination of , , and , all in one place.
useEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmountuseEffectuseEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmountuseContext
useContextuseContext
useContextThe Hook accepts a context object, which is the value returned from , and returns the current context value for that context. The useContext Hook also works with the React Context API in order to share data throughout the app without the need to pass your app props down through various levels.
useContextReact.createContextIt should be noted that the argument passed to the hook must be the context object itself and any component calling the always re-renders whenever the context value changes.
useContextuseContextuseContextReact.createContextuseContext需要注意的是,传递给 Hook的参数必须是上下文对象本身,并且每当上下文值发生变化时,调用的组件都会重新渲染。
useContextuseContextuseReducer
useReduceruseReducer
useReducerThe Hook gives an alternative to and is especially preferable to it when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one. It takes on a function and an initial state input and returns the current state and a function as output by means of array destructuring. also optimizes the performance of components that trigger deep updates.
useReducersetStatereducerdispatchuseReduceruseReducersetStatereducerdispatchuseReducerPros
优点
Fewer lines of code
代码行数更少
Hooks allows you group code by concern and functionality, and not by lifecycle. This makes the code not only cleaner and concise but also shorter. Below is a comparison of a simple stateful component of a searchable product data table using React, and how it looks in Hooks after using the keyword.
useStateHooks允许我们按关注点和功能对代码进行分组,而非按生命周期。这不仅让代码更简洁、更紧凑,还更短。以下是一个简单的可搜索产品数据表的有状态组件的对比,展示了使用React类组件和使用Hooks后的差异。
Stateful components
类组件实现
js
class TweetSearchResults extends React.Component {
constructor(props) {
super(props);
this.state = {
filterText: "",
inThisLocation: false,
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInThisLocationChange =
this.handleInThisLocationChange.bind(this);
}
handleFilterTextChange(filterText) {
this.setState({
filterText: filterText,
});
}
handleInThisLocationChange(inThisLocation) {
this.setState({
inThisLocation: inThisLocation,
});
}
render() {
return (
<div>
<SearchBar
filterText={this.state.filterText}
inThisLocation={this.state.inThisLocation}
onFilterTextChange={this.handleFilterTextChange}
onInThisLocationChange={this.handleInThisLocationChange}
/>
<TweetList
tweets={this.props.tweets}
filterText={this.state.filterText}
inThisLocation={this.state.inThisLocation}
/>
</div>
);
}
}Same component with Hooks
js
const TweetSearchResults = ({ tweets }) => {
const [filterText, setFilterText] = useState("");
const [inThisLocation, setInThisLocation] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inThisLocation={inThisLocation}
setFilterText={setFilterText}
setInThisLocation={setInThisLocation}
/>
<TweetList
tweets={tweets}
filterText={filterText}
inThisLocation={inThisLocation}
/>
</div>
);
};js
class TweetSearchResults extends React.Component {
constructor(props) {
super(props);
this.state = {
filterText: "",
inThisLocation: false,
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInThisLocationChange =
this.handleInThisLocationChange.bind(this);
}
handleFilterTextChange(filterText) {
this.setState({
filterText: filterText,
});
}
handleInThisLocationChange(inThisLocation) {
this.setState({
inThisLocation: inThisLocation,
});
}
render() {
return (
<div>
<SearchBar
filterText={this.state.filterText}
inThisLocation={this.state.inThisLocation}
onFilterTextChange={this.handleFilterTextChange}
onInThisLocationChange={this.handleInThisLocationChange}
/>
<TweetList
tweets={this.props.tweets}
filterText={this.state.filterText}
inThisLocation={this.state.inThisLocation}
/>
</div>
);
}
}使用Hooks的相同组件
js
const TweetSearchResults = ({ tweets }) => {
const [filterText, setFilterText] = useState("");
const [inThisLocation, setInThisLocation] = useState(false);
return (
<div>
<SearchBar
filterText={filterText}
inThisLocation={inThisLocation}
setFilterText={setFilterText}
setInThisLocation={setInThisLocation}
/>
<TweetList
tweets={tweets}
filterText={filterText}
inThisLocation={inThisLocation}
/>
</div>
);
};Simplifies complex components
简化复杂组件
JavaScript classes can be difficult to manage, hard to use with hot reloading and may not minify as well. React Hooks solves these problems and ensures functional programming is made easy. With the implementation of Hooks, We don't need to have class components.
JavaScript类难以管理,与热重载配合使用时存在问题,而且可能无法很好地压缩。React Hooks解决了这些问题,让函数式编程变得简单。通过使用Hooks,我们不再需要类组件。
Reusing stateful logic
复用有状态逻辑
Classes in JavaScript encourage multiple levels of inheritance that quickly increase overall complexity and potential for errors. However, Hooks allow you to use state, and other React features without writing a class. With React, you can always reuse stateful logic without the need to rewrite the code over and over again. This reduces the chances of errors and allows for composition with plain functions.
JavaScript类会鼓励多层继承,这会迅速增加整体复杂度和出错概率。然而,Hooks允许你在不编写类的情况下使用状态和其他React特性。借助React,你可以始终复用有状态逻辑,而无需重复编写代码。这减少了出错的可能性,并允许通过普通函数进行组合。
Sharing non-visual logic
共享非可视化逻辑
Until the implementation of Hooks, React had no way of extracting and sharing non-visual logic. This eventually led to more complexities, such as the HOC patterns and Render props, just to solve a common problem. But, the introduction of Hooks has solved this problem because it allows for the extraction of stateful logic to a simple JavaScript function.
There are of course some potential downsides to Hooks worth keeping in mind:
- Have to respect its rules, without a linter plugin, it is difficult to know which rule has been broken.
- Need a considerable time practicing to use properly (Exp: useEffect).
- Be aware of the wrong use (Exp: useCallback, useMemo).
Note (React 18+): Avoiding Unnecessary EffectsModern best practices refine how we use effects and event handlers. It's easy to overuse— the new React docs caution: "If there is no external system involved, you shouldn't need an Effect. Removing unnecessary Effects will make your code easier to follow, faster to run, and less error-prone." (react.dev)useEffectInstead of using an effect to set derived state (e.g.,), compute it directly in the component body:useEffect(() => setFullName(firstName + ' ' + lastName), [firstName, lastName]). Similarly, handle user events like form submission or logging directly in event handlers rather than in effects. This reduces double renders and "stale closure" bugs.const fullName = firstName + ' ' + lastNameAutomatic Memoization via React Compiler: New in React 19 is the React Optimizing Compiler (sometimes called React Forget) which can handle manyanduseCallbackoptimizations for you. When enabled, the compiler statically analyzes your component code and automatically hoists stable functions and memoizes values that don't need to change. The official React docs note that the compiler "handles memoization for you, eliminating the need for manual useMemo, useCallback, and React.memo" in many cases. Writing clean functional components (no side-effects in render, pure computations) will allow the compiler to maximize optimizations.useMemo
在Hooks出现之前,React没有提取和共享非可视化逻辑的方法。这最终导致了更多的复杂性,比如为了解决一个常见问题而使用HOC模式和渲染属性。但是,Hooks的引入解决了这个问题,因为它允许将有状态逻辑提取到一个简单的JavaScript函数中。
当然,Hooks也有一些需要注意的潜在缺点:
- 必须遵守其规则,如果没有linter插件,很难知道违反了哪条规则。
- 需要大量练习才能正确使用(例如)。
useEffect - 要注意错误用法(例如、
useCallback)。useMemo
注意(React 18+):避免不必要的副作用现代最佳实践优化了我们使用副作用和事件处理程序的方式。很容易过度使用——新的React文档警告:“如果不涉及外部系统,你应该不需要使用副作用。移除不必要的副作用会让你的代码更易于理解、运行更快且更不易出错。”(react.dev)useEffect不要使用副作用来设置派生状态(例如),而是直接在组件主体中计算:useEffect(() => setFullName(firstName + ' ' + lastName), [firstName, lastName])。同样,直接在事件处理程序中处理表单提交或日志记录等用户事件,而非在副作用中处理。这减少了重复渲染和“过时闭包”错误。const fullName = firstName + ' ' + lastName通过React Compiler实现自动记忆化:React 19中的新特性是React优化编译器(有时称为React Forget),它可以为你处理许多和useCallback优化。启用后,编译器会静态分析你的组件代码,并自动提升稳定函数并记忆不需要更改的值。官方React文档指出,编译器“为你处理记忆化,在许多情况下消除了手动使用useMemo、useMemo和useCallback的需求”。编写简洁的函数式组件(渲染时无副作用、纯计算)将使编译器最大化优化效果。React.memo
React Hooks vs Classes
React Hooks vs 类组件
When Hooks were introduced to React, it created a new problem: how do we know when to use function components with Hooks and class components? With the help of Hooks, it is possible to get state and partial lifecycle Hooks even in function components. Hooks also allow you to use local state and other React features without writing a class.
Here are some differences between Hooks and Classes to help you decide:
| React Hooks | Classes |
|---|---|
| It helps avoid multiple hierarchies and make code clearer | Generally, when you use HOC or renderProps, you have to restructure your App with multiple hierarchies when you try to see it in DevTools |
| It provides uniformity across React components. | Classes confuse both humans and machines due to the need to understand binding and the context in which functions are called. |
当Hooks被引入React时,产生了一个新问题:我们如何知道何时使用带Hooks的函数式组件,何时使用类组件?借助Hooks,即使在函数式组件中也可以获取状态和部分生命周期Hook。Hooks还允许你在不编写类的情况下使用本地状态和其他React特性。
以下是Hooks和类组件之间的一些差异,可帮助你做出选择:
| React Hooks | 类组件 |
|---|---|
| 避免多层嵌套,让代码更清晰 | 通常,当你使用HOC或渲染属性时,在开发者工具中查看应用时,你会发现应用被重构为多层嵌套结构 |
| 在React组件之间提供一致性 | 类组件会让人类和机器都感到困惑,因为需要理解绑定以及函数调用的上下文 |