React Custom Hooks

React Custom Hooks

React custom Hooks is a powerful and practical feature that can help developers separate logic and state from components, improving the reusability and logic abstraction of components. This paper will introduce the definition, usage and design principles of React custom Hooks, analyze its optimization effect on functional components, and demonstrate through examples how to use custom Hooks to improve component performance, reuse logic and achieve logic decoupling.

1 Overview of React Custom Hooks

1.1 Definition and advantages of Hooks

Hooks are an important feature introduced in React 16.8 version, which allow the use of state and other React features in functional components. Hooks are designed to solve some problems when using complex logic, shared state, and handling side effects in class components, making functional components more capable and flexible.

Hooks are special functions that allow you to "hook in" state, lifecycle, and other React features into React functional components. this.stateThey provide a way to use class-less components and allow you to use concepts similar to and in functional components this.props.

Hooks provide several specific API functions, the most commonly used ones include useState, useEffect, useContextand so on. These functions can be called inside functional components to handle state management, side effects, and other operations related to the logic of the component.

The main Hooks functions include:

  1. useState: Used to add and manage state in functional components. useStateFunctions return a state value and a function to update that state, allowing us to share and update state between components.

  2. useEffect: Used to handle side-effect operations, such as subscribing to data sources, network requests, event monitoring, etc. useEffectA function takes a side-effect function and executes that function when the component renders. It also cleans up side effects when components are updated or unmounted.

  3. useContext: Used to access the React context in functional components. useContextThe function receives a context object and returns its current value. It effectively removes the need to use static contextTypeand in class components this.context.

In addition to the above three commonly used Hooks functions, React also provides other Hooks functions, such as useReducer, useCallback, useMemo, useRefetc., to meet different needs and scenarios.

Advantages of Hooks:

  1. More concise and understandable code : Compared with traditional class components, using Hooks can write less and more concise code. Hooks make the logic more centralized, reduce the boilerplate code of the component, and improve the readability and maintainability of the code.

  2. Better reusable logic and state : By using custom Hooks, we can encapsulate reusable logic and state as a function, and then share it among multiple components. This way of code reuse avoids the problems of state transfer and repeated writing between components.

  3. More flexible component design : Hooks can be used to design components more flexibly without the limitation of class components. We can use state, side effects and other React features in functional components, making the logic of components more free and clear.

  4. Easier to test : Functional components and Hooks separate the logic and state of components, making testing easier and more direct. We can easily test the logic of components in a targeted manner without having to pay attention to the complex lifecycle and state management in class components.

React Hooks provides a new way of writing React components, through functional components and specific API functions, making component development easier, more efficient and more flexible. Hooks allow us to manage state, handle side effects, and share logic, while also improving code readability and maintainability. It is an important part of the React ecosystem, providing us with powerful tools and a simplified development process for building modern user interfaces.

1.2 Custom Hooks

Custom Hooks are a way to reuse logic in React. They allow us to extract component logic into reusable functions that can be shared across multiple components. Custom Hooks usually start with "use", such as "useForm" or "useTheme".

Custom Hooks can complete various functions, such as handling form state, handling side effects, handling network requests, etc.

Here is an example custom Hooks for handling form state:

import {
    
     useState } from "react";

function useForm(initialState) {
    
    
  const [values, setValues] = useState(initialState);

  const handleChange = (event) => {
    
    
    setValues({
    
     ...values, [event.target.name]: event.target.value });
  };

  const resetForm = () => {
    
    
    setValues(initialState);
  };

  return [values, handleChange, resetForm];
}

export default useForm;

In the above example, we use the useState hook to create a form state and return an array containing the form's value, a function to modify the form value, and a function to reset the form.

We can use it in components using custom Hooks:

import React from "react";
import useForm from "./useForm";

function MyForm() {
    
    
  const [values, handleChange, resetForm] = useForm({
    
     name: "", email: "" });

  const handleSubmit = (event) => {
    
    
    event.preventDefault();
    // 处理表单提交逻辑
  };

  return (
    <form onSubmit={
    
    handleSubmit}>
      <input type="text" name="name" value={
    
    values.name} onChange={
    
    handleChange} />
      <input type="email" name="email" value={
    
    values.email} />
      <button type="submit">提交</button>
      <button type="button" onClick={
    
    resetForm}>重置</button>
    </form>
  );
}

export default MyForm;

By using custom Hooks, we can extract form logic from components, making the code more reusable and concise. This allows us to easily use the same form logic in other components.

1.3 Design principles of custom Hooks

When designing custom Hooks, there are some principles that can help us write reusable, maintainable, and easy-to-understand code. Here are some principles for designing custom Hooks:

  1. Principle 1: Single Responsibility Principle (Single Responsibility Principle): Custom Hooks should focus on solving a specific problem or processing a specific logic. Avoid mixing too much functionality and logic in one custom Hooks, this can make it clearer, easier to test and reuse.

  2. Principle 2: Clear Function Signature : The function signature of custom Hooks should be clear and clear so that developers can easily understand and use it. Function parameters and return values ​​should have descriptive names, and provide necessary documentation or comments.

  3. Principle 3: Naming Convention : Follow the naming convention of React Hooks, start with "use" and use camel case naming. Doing so can make custom Hooks consistent with React's built-in Hooks, making it easy for developers to identify and use them.

  4. Principle 4: Configurability : Custom Hooks should provide enough configuration options to meet different scenarios and needs. Custom Hooks can be made more flexible and customizable by parameterizing them.

  5. Principle 5: Testability : Custom Hooks should be easy to test, and both unit tests and integration tests should be able to cover the functions of custom Hooks. This can be achieved by separating logic from side effects, providing a clear function interface, etc.

  6. Principle 6: Documentation and Comments : Provide clear documentation and comments in the code of custom Hooks, explaining the purpose, parameters, return values ​​and usage of custom Hooks. This helps other developers understand and use custom Hooks correctly.

  7. Principle 7: Follow Hooks Rules (Follow Hooks Rules): Custom Hooks should follow the rules of React Hooks to ensure that only Hooks provided by React are used inside custom Hooks. In addition, avoid calling Hooks in conditional judgments, loops, or nested functions to ensure that the execution order of Hooks will not change.

  8. Principle Eight: Good Naming and Abstraction : Through good naming and abstraction, the purpose and function of custom Hooks are as clear as possible. Reasonable naming and moderate abstraction can improve the readability and maintainability of the code.

Following these design principles can help us write high-quality custom Hooks that are reusable, testable, and easy to understand and maintain.

2 Using React to customize Hooks

2.1 Naming conventions and conventions of custom Hooks

The naming conventions and conventions of custom Hooks are as follows:

  1. Naming should accurately describe the function of the Hooks: you can start with a verb, such as useFetchDataor useLocalStorage, or use a noun to describe the function, such as useScrollPositionor useWindowSize.

  2. Use usea prefix: In order to distinguish it from ordinary functions, the names of custom Hooks should usestart with .

  3. Use CamelCase: Custom Hooks should be named using CamelCase, with the first letter of each word capitalized, eg useFetchData.

  4. The parameters of Hooks should optionsend with: If you need to pass parameters to Hooks, the parameter name should optionsend with, eg useFetchDataOptions.

  5. The return value should conform to the convention: Hooks should return an array or object containing the relevant state and handler functions. For example, a Hook with state can return an array: [state, setState], or an object with multiple states and handlers: {state1, state2, handler1, handler2}.

  6. Follow the convention when using custom Hooks: When calling a custom Hook, constvariables should be declared with a keyword and start with use, to let the reader know that this is a custom Hook. For example: const useFetchData = useFetchData().

  7. Store the Hook file in use 了更好地组织代码,将自定义 Hooks 的文件存储在以use 开头的目录中,例如:src/hooks/useFetchData.js`.

The naming conventions and conventions of custom Hooks can be summarized as: usestart with , use camel case, accurately describe the function, parameters end with , return value conforms to the convention, and declare variables with keywords optionswhen used .const

2.2 How to define and use custom Hooks

A custom Hook is a function whose name usestarts with and returns an array. It allows you to reuse code logic in functional components, and it can be used like Hooks that comes with React. Here are the steps to define and use custom Hooks:

  1. Define custom Hooks:
import { useState, useEffect } from 'react';

function useCustomHook() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  const increment = () => {
    setCount(prevCount => prevCount + 1);
  };

  return [count, increment];
}

The custom Hook above is named useCustomHook, which defines a countstate variable, and a incrementfunction that increments countthe value. useEffectListen for changes in and countdisplay countthe value on the page title.

  1. Use custom Hooks:
import React from 'react';
import useCustomHook from './useCustomHook';

function App() {
  const [count, increment] = useCustomHook();
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

export default App;

In the above example, by calling useCustomHookthe custom Hook, assign the returned countand incrementto Appthe variables in the component, use these variables in JSX, display the value of the counter and click the button to increase the counter.

In this way, we can reuse the logic in different function components useCustomHook, making the code more modular and reusable.

2.3 Common Application Scenarios of Custom Hooks

Custom Hooks can be applied in many different scenarios, the following are some common application scenarios:

  1. State management : Custom Hooks can be used to encapsulate state management logic, enabling multiple components to share and manage the same state. For example, you can create custom Hooks to handle global application state, user authentication state, or state management for form fields.

  2. Side-effect processing : Custom Hooks can encapsulate the logic of handling side-effect operations, such as data subscription, network request, local storage, etc. By customizing Hooks, code related to side effects can be shared among multiple components, reducing duplication of work. For example, you can create a custom Hooks to handle the logic of data fetching, timer operation or subscribing to events.

  3. Data acquisition and processing : Custom Hooks can be used to encapsulate the logic of data acquisition and processing for use in components. This allows components to focus more on rendering and interaction logic. For example, you can create a custom Hooks to get data from the API, transform the data or cache the data.

  4. Form processing : Custom Hooks can be used to process form logic, including form validation, form submission, form reset, etc. By customizing Hooks, form-related logic can be abstracted, making form processing simpler and reusable. For example, a custom Hook can be created to handle form validation and submission logic.

  5. Timers and animation effects : Custom Hooks can be used to handle the logic of timers and animation effects. By customizing Hooks, the logic related to the timer can be processed centrally, and operations such as starting, pausing, and stopping the timer can be realized. Likewise, common animation logic can be encapsulated to make it reusable across multiple components.

  6. Access browser API : Custom Hooks can be used to encapsulate the logic of accessing browser API, such as obtaining geographic location information, accessing local storage, processing browser history, etc. By customizing Hooks, these browser APIs can be conveniently used in components, providing a more concise interface and better reusability.

  7. Encapsulation of complex logic : Custom Hooks can also be used to encapsulate code blocks that process complex logic, making them reusable in multiple components. For example, you can create a custom Hooks to handle paging logic, sorting logic, permission control logic, etc., so as to avoid duplicating these logics in multiple components.

These are just a few examples of use cases for custom Hooks, in fact, custom Hooks can be used in a wide range of applications and can be used in almost any situation where shared logic is required. By making reasonable use of custom Hooks, we can improve the maintainability, reusability and readability of the code, making the development process more efficient and enjoyable.

3 Optimization function of React custom Hooks

3.1 Component performance optimization

3.1.1 Avoid unnecessary rendering

When rendering a component, avoiding unnecessary rendering can improve the performance of the component. Some common methods include:

  1. Use shouldComponentUpdatethe life cycle method : By rewriting the method in the component shouldComponentUpdate, and comparing the values ​​of props and state before and after in the method, you can decide whether to perform the next rendering. If the values ​​before and after are the same, false can be returned to avoid unnecessary rendering.

  2. UsePureComponent : PureComponentIt is a built-in component in React, it will automatically compare the value of props and state every time it is rendered, and decide whether to render according to the comparison result. Use logic PureComponentthat avoids manual implementation .shouldComponentUpdate

  3. UseReact.memo : React.memois a higher-order component for React that shallow compares components and prevents unnecessary rendering when props haven't changed. Just pass the component as a parameter to React.memo.

  4. Pass fewer props : If a component only needs a subset of props to render, you can avoid passing the entire props object to the component, and instead pass only the required properties.

  5. Avoid creating new objects or functions in the render method : Since the render method is called frequently, if you create new objects or functions in the render method, it may cause frequent garbage collections. These objects or functions can be moved to groups. Use event delegation: If a parent component contains multiple child components, and each child component has similar event handling logic, you can elevate the event handling logic to the parent component and pass the event to the child components through event delegation.

  6. Avoid frequent setState calls : setState is asynchronous and batched, but calling setState multiple times in a short period of time may result in multiple unnecessary renders. You can use setStatethe callback function or setStatethe function parameter of the function to reduce unnecessary rendering.

3.1.2 Reduce Duplicate Code

Reducing duplicate code is an important aspect when optimizing component performance. Duplicated code increases maintenance costs and can lead to bugs and performance issues. Here are some ways to reduce repetitive code:

  1. Extract duplicate logic to functions or components : If the same logic code exists in multiple components, this part of code can be extracted into an independent function or component for sharing by multiple components. This reduces code duplication and increases code maintainability and reusability.

  2. Using Higher-Order Components : A Higher-Order Component is a function that takes a component as an argument and returns a new component. By using higher-order components, the same functional logic can be shared among multiple components. This reduces duplication of code and encapsulates common logic into a single higher-order component.

  3. Use the Render Props pattern : Render Props is a pattern for passing functional logic to child components through component properties. By encapsulating a portion of logic into a Render Props component, the same logic can be reused across multiple components. This reduces duplication of code and improves code maintainability.

  4. Abstract common components : If multiple components have similar UI structures and functions, they can be abstracted as common components to reduce duplication of code. By abstracting common components, the same UI and functions can be used in different contexts, improving code reusability and maintainability.

  5. Use Hooks : If there is duplicate logic or side-effect code, it can be abstracted as custom Hooks and shared among multiple components. Hooks provide a more concise, composable and reusable way to handle logic in components. By using Hooks, you can reduce repetitive code and extract logic from components to improve code readability and maintainability.

  6. Use inheritance wisely : Inheritance is a way of sharing functionality between components, but it needs to be used with care. Reasonable use of inheritance can avoid duplication of code and allow subcomponents to customize and extend functionality. But excessive use of inheritance can lead to tight coupling between components and hard-to-understand code.

By reducing duplicate code, you can improve code maintainability, readability, and reusability, thereby reducing performance problems and errors. These methods can be selected and used according to specific situations, and specific optimization can be carried out according to the common characteristics and functional requirements among components.

3.2 Logic reuse and abstraction capabilities

3.2.1 Shared state logic

Shared state logic is an important aspect when optimizing component performance. Shared state logic refers to sharing the same state or data logic among multiple components. Here are some ways to implement shared state logic:

  1. Lift state to a shared container component : If multiple components need to access and update the same state, they can lift that state into their common parent component and pass the state to them as props. This way, multiple subcomponents can share the same state and be able to communicate with each other.

  2. Using the React Context API : React's Context API allows state to be shared across a tree of components, and allows multiple components to subscribe to that shared state. By creating a Context object and setting the value of the shared state in the provider (Provider) component, other components can use the state value to access the shared state through the consumer (Consumer) component.

  3. Use Redux or another state management library : Redux is a commonly used state management library that provides a centralized state management solution. Use Redux to share and manage global state across your application. Other state management libraries, such as Mobx, Vuex, etc., also provide similar functionality.

  4. Use useReducer Hook : useReducer is a state management Hook provided by React, which can be used to manage the state logic of components and encapsulate the update logic as a reducer function. The same state can be shared and manipulated across multiple components by passing state and update functions to other components.

  5. Use third-party state management tools : In addition to Redux and React Context, there are many third-party state management tools that can be used to share state logic. For example, Mobx, Zustand, Recoil, etc. Using these tools makes it easier to manage and share state logic.

By sharing state logic, state and data logic can be separated from components, improving code maintainability and reusability. Multiple components can share the same state, and manage and update the state uniformly. But when sharing state logic, also be careful to avoid excessive sharing of state and increased complexity. Reasonable state sharing can improve application performance and development efficiency, but improper sharing may lead to code confusion and maintenance difficulties.

3.2.2 Encapsulating complex logic

Encapsulating complex logic is a common requirement in component development, which can improve code maintainability and reusability. Here are some general methods for encapsulating complex logic:

  1. Create custom Hooks : Custom Hooks are a common way to encapsulate complex logic. By abstracting complex logic into reusable custom Hooks, that logic can be shared and reused across multiple components. Custom Hooks can contain multiple states, effects, and other logic codes, providing a concise, composable, and extensible way to handle complex logic.

  2. Using Higher Order Components (HOC) : A higher order component is a function that takes a component and returns a new component. By using higher-order components, common complex logic can be encapsulated in a function, which can then be applied to multiple components. This avoids duplicating the same logic code in each component.

  3. Use the Render Props pattern : The Render Props pattern is implemented by passing common logic between components as functions to child components. Complex logic can be shared across multiple components by encapsulating logic in reusable Render Props components and then passing that logic to other components via children or render props.

  4. Abstract common components : If multiple components have similar UI structures and functions, they can be abstracted as common components. Common components can encapsulate complex logic and provide customized configuration through the props interface to achieve reuse in different scenarios.

  5. Use utility functions and auxiliary classes : For some independent and reusable complex logic, it can be encapsulated as utility functions or auxiliary classes. This allows complex logic to be used through function or method calls without duplicating code.

No matter which way you choose to encapsulate complex logic, the key is to abstract the logic into reusable modules to improve code readability, maintainability, and reusability. Encapsulating complex logic can improve development efficiency, reduce code duplication, and better organize and manage code. According to specific scenarios and needs, you can choose the most suitable encapsulation method to handle complex logic.

4 Use examples to demonstrate the application of React custom Hooks

4.1 Example 1: Form Validation Hooks

When it comes to form validation, custom Hooks can be used to encapsulate complex validation logic, making it easier to reuse and maintain. Here is an example showing how to use custom Hooks for form validation:

import {
    
     useState } from 'react';

// 自定义表单验证Hooks
const useFormValidator = () => {
    
    
  const [values, setValues] = useState({
    
    });
  const [errors, setErrors] = useState({
    
    });

  // 处理表单字段的变化
  const handleChange = (e) => {
    
    
    const {
    
     name, value } = e.target;
    setValues((prevValues) => ({
    
    
      ...prevValues,
      [name]: value,
    }));
  };

  // 处理表单提交
  const handleSubmit = (e) => {
    
    
    e.preventDefault();
    // 执行验证逻辑
    const validationErrors = validate(values);
    setErrors(validationErrors);
    if (Object.keys(validationErrors).length === 0) {
    
    
      // 验证通过,执行提交逻辑
      submitForm(values);
    }
  };

  // 表单字段验证逻辑
  const validate = (values) => {
    
    
    let errors = {
    
    };

    // 进行具体的验证逻辑,根据需要添加更多验证规则
    if (!values.username) {
    
    
      errors.username = '请填写用户名';
    }

    if (!values.email) {
    
    
      errors.email = '请填写邮箱';
    } else if (!isValidEmail(values.email)) {
    
    
      errors.email = '请输入有效的邮箱地址';
    }

    // 返回验证错误信息
    return errors;
  };

  // 判断邮箱地址是否有效
  const isValidEmail = (email) => {
    
    
    // 简单的邮箱验证逻辑,根据需要可以进行更复杂的验证
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  };

  return {
    
     values, errors, handleChange, handleSubmit };
};

// 使用自定义Hooks进行表单验证
function LoginForm() {
    
    
  const {
    
     values, errors, handleChange, handleSubmit } = useFormValidator();

  return (
    <form onSubmit={
    
    handleSubmit}>
      <input type="text" name="username" value={
    
    values.username || ''} onChange={
    
    handleChange} />
      {
    
    errors.username && <span>{
    
    errors.username}</span>}
      <input type="email" name="email" value={
    
    values.email || ''} onChange={
    
    handleChange} />
      {
    
    errors.email && <span>{
    
    errors.email}</span>}
      <button type="submit">提交</button>
    </form>
  );
}

With the above example, we created a useFormValidatorcustom Hook named Hook that encapsulates the logic of form validation. In LoginFormthe component, we use this custom Hook to handle form validation. When the form field changes, we handleChangeupdate the value of the form field through the method; when the form is submitted, we execute the validation logic and update the error information according to the validation result, and perform the actual form submission operation when there is no error.

In this way, we can use custom Hooks in multiple components useFormValidatorto handle form validation, which provides a reusable way to manage complex form validation logic. We can easily add more validation rules and use this custom Hooks directly when we need to validate the form, thus reducing the duplication of code writing and maintenance costs.

4.2 Example 2: Using custom Hooks to realize communication between components

When communication between multiple components is required, custom Hooks can be used to encapsulate the communication logic to achieve state sharing and message passing between components. Here is an example showing how to use custom Hooks to achieve inter-component communication:

import {
    
     useState, useEffect } from 'react';

// 自定义通信Hooks
const useCommunication = () => {
    
    
  const [message, setMessage] = useState('');

  // 发送消息的函数
  const sendMessage = (msg) => {
    
    
    setMessage(msg);
  };

  return {
    
     message, sendMessage };
};

// 接收消息的组件
function MessageReceiver() {
    
    
  const {
    
     message } = useCommunication();

  return <div>{
    
    message}</div>;
}

// 发送消息的组件
function MessageSender() {
    
    
  const {
    
     sendMessage } = useCommunication();

  useEffect(() => {
    
    
    // 模拟发送消息的动作
    sendMessage('Hello, MessageReceiver!');
  }, [sendMessage]);

  return <button>发送消息</button>;
}

In the above example, we created a useCommunicationcustom Hook named Hook that encapsulates the logic for inter-component communication. In MessageReceiverthe component, we use the custom Hooks to receive the message and display the content of the message on the page. In MessageSenderthe component, we send messages through the custom Hooks, and automatically send a message after the component is mounted.

By using useCommunicationcustom Hooks, we can achieve simple messaging in MessageReceiverand between components. MessageSenderIn this way, we can use the same custom Hooks in other components of the application to achieve state sharing and communication between components. This approach makes the communication logic between components clearer, more maintainable, and provides better component reusability.

When communication between multiple components is required, custom Hooks can be created in a similar manner and used in each component to implement the required communication logic. This encapsulation method can reduce code redundancy, improve code readability and maintainability, and better organize and manage communication logic between components.

5 Conclusion

Custom Hooks are an effective way to encapsulate complex logic and implement communication between components. By using custom Hooks, complex logic or communication logic can be abstracted into reusable modules to improve code maintainability and reusability. This avoids redundancy and duplication of code, while providing a concise, composable, and extensible way to handle complex logic and implement communication between components.

Custom Hooks that encapsulate complex logic can separate logic from components, allowing components to focus more on UI rendering and interaction. By customizing Hooks, some common logic codes can be encapsulated to improve the readability, maintainability and testability of the code. Custom Hooks can also provide better code reusability, the same logic can be shared and used in multiple components.

In addition, custom Hooks can also be used to implement communication between components. By customizing Hooks, the state sharing logic between components can be encapsulated, and communication mechanisms such as message passing and event triggering between components can be realized. Custom Hooks provide a reusable and unified way for inter-component communication, making the communication logic between components clearer and more maintainable.

Custom Hooks that encapsulate complex logic and enable communication between components can improve code maintainability, reusability, and composability. Using custom Hooks can simplify code, reduce redundancy, and improve development efficiency. It is one of the important tools for developing high-quality and scalable components.

Guess you like

Origin blog.csdn.net/weixin_55756734/article/details/131523186