In React
, data flows unidirectionally from top to bottom, that is, from parent components to child components. Component data is stored in props
and state
. In fact, data is essential in any application. We need to directly change a section of the page to update the view, or indirectly change the data elsewhere. In React
, the props
and state
properties are used to store data. The main purpose of state
is to save, control, and modify the mutable state of the component. state
is initialized within the component, can be modified by the component itself and cannot be accessed or modified externally. state
can be considered as a local data source controlled only by the component itself. For React
functional components, useState
is the hook function used to manage the component's own state.
Regarding the meaning of React Hooks
, Ruan Yifeng explained that the meaning of React Hooks
is to write components as pure functions as much as possible. If external functionality and side effects are needed, use hooks to hook in external code. React Hooks
are those hooks. I think this explanation is very appropriate. Taking useState
as an example, when writing functional components, this function is hooked in. Inside this function, some internal scoped variables are saved, which is also known as a closure. Therefore, Hooks
can also be understood as hooking in another scoped variable and function logic to be used in the current scope.
As for why to use React Hooks
, the summary is still for component reusability, especially in more granular component reusability, React Hooks
perform better. In React
, there is an endless array of solutions for code reusability, but overall, code reusability is still very complex. A large part of this complexity is because fine-grained code reusability should not be tied to component reusability. Solutions based on component composition such as HOC
and Render Props
essentially wrap the reusable logic into components and then utilize component reusability to achieve logic reusability, naturally limiting the component reusability, resulting in limited extensibility, Ref
barriers, Wrapper Hell
, and other issues. Therefore, we need a simple and direct way of code reusability. Functions are the most direct and cost-effective way to abstract reusable logic into functions should be the most straightforward and cost-effective way to reuse code. However, for state logic, some abstract patterns (such as Observable
) are still needed to achieve reusability. This is precisely the concept behind Hooks
, to treat functions as the smallest unit of code reusability, while incorporating built-in patterns to simplify the reuse of state logic. Compared to the aforementioned approaches, Hooks
decouple the logic reusability within components from the reusability of components, truly attempting to solve the problem of fine-grained logic reuse (between components) from the bottom layer. Additionally, this declarative logic reuse approach extends the explicit data flow between components and the concept of composition further into the components.
The motivation for using React Hooks
is explained by the official documentation as follows:
Hooks
solve a variety of seemingly unrelated problems we encountered while writing and maintaining react
for the past five years. Whether you are learning react
, using it every day, or even if you are only using a framework with a component model similar to React
, you will more or less notice these issues.
Reusable stateful logic across components is very difficult. React
does not provide a way to attach reusable behavior to a component, such as connecting it to a store
like the connect
method in state management libraries like redux
. If you have been using React
for a while, you may be familiar with patterns like render props and higher-order components to try to solve these problems, but these patterns require you to refactor your components when using them, which can be cumbersome and make the code harder to maintain. With Hooks
, you can extract stateful logic from a component so it can be tested independently and reused. If you look at a typical React
application in React DevTools
, you may find a wrapper hell composed of components containing providers, consumers, higher-order components, render props, and other abstraction layers. Although we can filter them out in DevTools
, it reflects a deeper problem: React
needs a better native way to share stateful logic. With Hooks
, you can extract the stateful logic from the components, making it easier to test and reuse. At the same time, Hooks
allow you to reuse stateful logic without changing the component structure, making it easier to share Hook
among many components or with the community.
Complex components become hard to understand. We often end up maintaining components that started out very simple but slowly evolved into an unmanageable mess of stateful logic and a bunch of side-effectful components. As development progresses, they become larger and messier, with various logic scattered throughout the components, and each lifecycle hook containing a bunch of unrelated logic. For example, our component might perform some data fetching work in componentDidMount
and componentDidUpdate
, but the same componentDidMount
method might also contain some unrelated logic, such as setting up event listeners (which later need to be cleaned up in componentWillUnmount). Related code is split, but completely unrelated code ends up being combined into one method, making it too easy to introduce errors and inconsistencies. The end result is that tightly related code is separated, while unrelated code is combined, which can easily lead to bugs and exceptions. In many cases, we are not likely to decompose these components into smaller ones because stateful logic is everywhere, and testing them is also very difficult. This is also one of the reasons why many people like to combine
React with a state management library. However, this usually introduces too much abstraction, requiring you to switch between different files and making reusing components more difficult. Therefore,
Hooks allow you to split a component into smaller functions based on related pieces (such as setting up subscriptions or fetching data) without forcing you to break it into lifecycle methods. You can also opt to use a
reducer` to manage the component's local state to make it more predictable.
Hard-to-understand class
. Apart from the difficulty in code reuse and management, we also found that class
is a major barrier to learning React
. You have to understand how this
works in JavaScript
, which is vastly different from other languages, and not to mention binding event handlers. Without a stable syntax proposal, this code becomes very redundant. People can understand props
, state
, and the top-down data flow very well, but when it comes to class
, they are at a loss. Even among experienced React
developers, there are differences in understanding the differences between function components and class
components, as well as the scenarios for using the two types of components. In addition, React
has been released for five years, and we hope it can keep up with the times for the next five years, just like other libraries such as Svelte
, Angular
, Glimmer
, etc., have shown. Component pre-compilation has huge potential, especially when it is not limited to templates. Recently, we have been experimenting with Prepack
to try component folding
, and have made some initial progress. However, we found that using class
components inadvertently encourages developers to use some solutions that render optimization measures ineffective. class
also brings some problems to the current tools, for example, class
cannot be compressed well and can cause unstable situations in hot reloading. Therefore, we want to provide an API that makes code easier to optimize. To solve these problems, Hooks
allow you to use more React
features in a non-class
scenario. In concept, React
components have always been more like functions, and Hooks
embrace functions while also not sacrificing the spirit of React
. Hooks
provide a solution to the problem without the need to learn complex functional or reactive programming techniques.
The simplest use of useState
is as follows: https://codesandbox.io/s/fancy-dust-kbd1i?file=/src/App.tsx
.
// use-my-state-version-1.ts import { forceRefresh } from "./index";
let saveState: any = null;
export function useMyState(state: T): [T, (newState: T) => void] { saveState = saveState || state; const rtnState: T = saveState; const setState = (newState: T): void => { saveState = newState; forceRefresh(); }; return [rtnState, setState]; }
// App.tsx import { useMyState } from "./use-my-state-version-1"; import "./styles.css";
export default function App() { const [count, setCount] = useMyState(0);
console.log("refresh"); const addCount = () => setCount(count + 1);
return ( <> {count} Count++ </> ); }
Of course, this is just a simple implementation of useState
. For the true implementation of React
, you can refer to packages/react-reconciler/src/ReactFiberHooks.js
. The current version of React
is 16.10.2
. You can also take a brief look at the related type
.