>  기사  >  웹 프론트엔드  >  React의 커스텀 Hook에 대한 심층적인 이해

React의 커스텀 Hook에 대한 심층적인 이해

青灯夜游
青灯夜游앞으로
2023-04-20 18:22:541879검색

React의 커스텀 Hook에 대한 심층적인 이해

React 프로젝트에서는 useState, useContext 및 useEffect와 같이 React와 함께 제공되는 여러 내장 Hook을 사용하는 경우가 많습니다. 그러나 때로는 특정 목적을 가진 Hook을 원할 수도 있습니다. 예를 들어 데이터를 얻기 위해 useData, 연결을 얻기 위해 useConnect 등이 있습니다. 이러한 Hooks는 React에서 찾을 수 없지만 React는 필요에 맞는 사용자 정의 Hooks를 생성할 수 있는 매우 유연한 방법을 제공합니다.

훅을 사용자 정의하는 방법

React에서는 다음 명명 규칙을 따라야 합니다.

  • React 구성 요소: React 구성 요소 이름은 StatusBar 및 SaveButton과 같이 대문자로 시작해야 합니다. React 구성요소는 또한 와 같이 React가 렌더링하는 방법을 알고 있는 것을 반환하기 위해 JSX 가 필요합니다.

  • React 후크: 후크 이름은 use로 시작하고 그 뒤에 대문자가 와야 합니다(예: useState(내장) 또는 useStatus(사용자 정의)). React 구성요소와 달리 사용자 정의 Hooks는 모든 값을 반환할 수 있습니다.

이 명명 규칙을 사용하면 언제든지 구성 요소를 보고 상태, 효과 및 React 기능에 의해 "숨겨질" 수 있는 기타 위치를 이해할 수 있습니다. 예를 들어, 구성 요소에서 getColor() 함수 호출을 보면 해당 이름이 use로 시작하지 않기 때문에 React 상태를 포함할 수 없다는 것을 확신할 수 있습니다. 그러나 useStatus()와 같은 함수 호출에는 다른 Hooks에 대한 호출이 포함될 가능성이 높습니다!

컴포넌트 간 공유 로직

그 안의 코드는 어떻게 하기보다는 무엇을 하고 싶은지를 설명합니다.

커스텀 Hooks의 핵심은 컴포넌트 간 공유 로직입니다. 사용자 정의 Hooks를 사용하면 중복 논리를 줄일 수 있으며 더 중요한 것은 사용자 정의 Hooks 내부의 코드가 수행 방법이 아니라 수행하려는 작업을 설명한다는 것입니다. 사용자 정의 Hooks로 로직을 추출할 때 특정 "외부 시스템" 또는 브라우저 API 호출이 처리되는 방법에 대한 세부 사항숨길 수 있습니다. 그러면 구성 요소의 코드가 구현 세부 정보가 아닌 의도를 표현할 수 있습니다. 다음은 간단한 예입니다.

import { useState } from 'react';
function useCounter(initialValue) {
  const [count, setCount] = useState(initialValue);
  function increment() {
    setCount(count + 1);
  }
  return [count, increment];
}

이 사용자 정의 후크는

이라고 하며, 초기 값을 매개변수로 받아들이고 현재 카운트 값과 카운트를 증가시키는 함수가 포함된 배열을 반환합니다. 커스텀 Hook을 사용하는 것은 매우 간단합니다. 함수 컴포넌트에서 호출하기만 하면 됩니다. 다음은 useCounter 사용 예입니다. useCounter 的例子:

import React from 'react';
import useCounter from './useCounter';

function Counter() {
  const [count, increment] = useCounter(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

在这个例子中,我们导入了 useCounter,并在组件中调用它。我们将返回的数组解构为 count 和 increment

import useCounter from &#39;./useCounter&#39;;
function Counter() {
	const [count1, increment1] = useCounter(0);
	const [count2, increment2] = useCounter(100);
  return (
    <div>
      <p>Count1: {count1}</p>
      <button onClick={increment1}>Increment1</button>
       <p>Count2: {count2}</p>
      <button onClick={increment2}>Increment2</button>
    </div>
  );
}

이 예에서는

를 가져와서 구성 요소에서 호출했습니다. 반환된 배열을 countincrement로 분해하여 구성 요소에서 사용합니다.

사용자 정의 후크를 사용하면 상태 저장 논리를 공유할 수 있지만 상태 자체는 공유할 수 없습니다. useCounter 为例:

import { useState, useEffect } from &#39;react&#39;;
function useWindowWidth() {
  const [windowWidth, setWindowWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWindowWidth(window.innerWidth);
    window.addEventListener(&#39;resize&#39;, handleResize);
    return () => window.removeEventListener(&#39;resize&#39;, handleResize);
  }, []);
  return windowWidth;
}

当我们点击 Increment2 时,并不会影响 count1 ,因为每一个 useCounter 的调用都是独立的,其内部状态也是独立的。

分类

功能型 Hooks

以实现特定功能或目的,与具体业务无关:

useWindowWidth

该 hook 返回窗口宽度的值。

import { useState } from &#39;react&#39;;

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.log(error);
    }
  };

  return [storedValue, setValue];
}

useLocalStorage

该 hook 允许你在本地存储中存储和检索值。

import { useState, useEffect } from &#39;react&#39;;
function useFetch(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
      } catch (error) {
        setError(error);
      } finally {
        setIsLoading(false);
      }
    };
    fetchData();
  }, [url]);
  return { data, error, isLoading };
}

业务型 Hooks

useFetch

该 hook 允许你从 API 中获取数据。

//useFetch.js
import {useState, useEffect} from &#39;react&#39;
//don&#39;t forget to give a url parameter for the function.
const useFetch = (url)=>{
  const [data, setData] = useState([])
  const getData = async ()=>{
    const response = await fetch(url)
    const userdata = await response.json()
    setData(userdata)
  }
 useEffect(()=>{
    getData()
  },[url])
  //return data that we will need in other components.
  return {data};
}
export default useFetch;

useModal

该 hook 允许你管理模态对话框的状态。

//useUserInfo.jsx
import { useEffect,useState } from &#39;react&#39;
const useUserInfo = (userId) => {
  const [userInfo, setUserInfo] = useState({})
  useEffect(() => {
    fetch(&#39;/user&#39;)
      .then(res => res.json())
      .then(data => setUserInfo(data))
  }, [userId])
  return userInfo
}
//Home.jsx
...
const Home = ()=>{
	const [userId,setUserId] = useState(&#39;103&#39;)
	const useInfo = useUserInfo(userId)
	return (
	  <>
	     <div>name:{userInfo.name}</div>
	     <div>age:{userInfo.age}</div>
	     ...
	  </> 
	)
}

在多个 Hook 之间传递信息

由于 Hook 本身就是函数,因此我们可以在它们之间传递信息。下面我们以 useUserInfo 获取用户信息 为例:

const [userId,setUserId] = useState(&#39;103&#39;)
const userInfo = useUserInfo(userId)

我们将 用户 id 保存在 userId 状态变量中,当用户进行某一操作 setUserId 时,由于 useState 为我们提供了 userId 状态变量的最新值,因此我们可以将它作为参数传递给自定义的 useUserInfo Hook:

export function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      showNotification(&#39;New message: &#39; + msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
}

此时,我们的 userInfo

사용자 정의 후크를 사용하면 상태 저장 논리를 공유할 수 있지만 상태 자체는 공유할 수 없습니다. Hook에 대한 각 호출은 동일한 Hook에 대한 다른 호출과 완전히 독립적입니다. 위의

를 예로 들어보세요.

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState(&#39;https://localhost:1234&#39;);
  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl,
    onReceiveMessage(msg) {
      showNotification(&#39;New message: &#39; + msg);
    }
  });
  // ...
Increment2를 클릭하면 각

호출이 독립적이고 내부 상태도 독립적이기 때문에 count1에 영향을 주지 않습니다.

분류

기능적 후크🎜특정 비즈니스에 관계없이 특정 기능이나 목적을 달성하려면:🎜

useWindowWidth🎜🎜이 후크는 창 너비 값을 반환합니다. 🎜
export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      onReceiveMessage(msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl, onReceiveMessage]); // ✅ All dependencies declared
}

useLocalStorage🎜🎜이 후크를 사용하면 로컬 저장소에서 값을 저장하고 검색할 수 있습니다. 🎜
import { useEffect, useEffectEvent } from &#39;react&#39;;  
// ...
export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  const onMessage = useEffectEvent(onReceiveMessage);
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      onMessage(msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ All dependencies declared
}

비즈니스 후크🎜

useFetch🎜🎜이 후크를 사용하면 API에서 데이터를 가져올 수 있습니다. 🎜rrreee

useModal🎜🎜이 후크를 사용하면 모달 대화 상자의 상태를 관리할 수 있습니다. 🎜rrreee

여러 Hook 간에 정보 전달🎜🎜Hook 자체가 함수이므로 후크 간에 정보를 전달할 수 있습니다. 아래에서는 사용자 정보를 얻기 위한 예시로 useUserInfo를 사용합니다. 🎜rrreee🎜사용자가 특정 작업 을 수행할 때 <code>userId 상태 변수에 사용자 ID를 저장합니다. setUserId code>, useStateuserId 상태 변수의 최신 값을 제공하므로 이를 사용자 정의 useUserInfo에 매개변수로 전달할 수 있습니다. code > Hook: 🎜rrreee🎜 이때 userId 변경에 따라 <code>userInfo가 업데이트됩니다. 🎜🎜이벤트 핸들러를 사용자 정의 Hooks에 전달🎜🎜🎜이 섹션에서는 React의 안정적인 버전에서 아직 출시되지 않은 🎜실험적🎜 API에 대해 설명합니다. 이 섹션에서는 React의 안정적인 버전에서 아직 출시되지 않은 실험적인 API에 대해 설명합니다. 🎜

你可能希望让组件自定义其行为,而不是完全地将逻辑封装 Hooks 中,我们可以通过将 event handlers 作为参数传递给 Hooks,下面是一个聊天室的例子:useChatRoom 接受一个服务端 url 和 roomId,当调用这个 Hook 的时候,会进行连接,

export function useChatRoom({ serverUrl, roomId }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      showNotification(&#39;New message: &#39; + msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]);
}

假设当连接成功时,你想将此逻辑移回你的组件:

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState(&#39;https://localhost:1234&#39;);
  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl,
    onReceiveMessage(msg) {
      showNotification(&#39;New message: &#39; + msg);
    }
  });
  // ...

要做到这一点,改变你的自定义 Hook ,把 onReceiveMessage 作为它的命名选项之一:

export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      onReceiveMessage(msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl, onReceiveMessage]); // ✅ All dependencies declared
}

这可以工作,但是当你的自定义 Hook 接受事件处理程序时,你还可以做一个改进。 在 onReceiveMessage 上添加依赖并不理想,因为它会导致每次组件重新渲染时聊天都重新连接。将此事件处理程序包装到 EffectEvent 中以将其从依赖项中移除:

import { useEffect, useEffectEvent } from &#39;react&#39;;  
// ...
export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
  const onMessage = useEffectEvent(onReceiveMessage);
  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.connect();
    connection.on(&#39;message&#39;, (msg) => {
      onMessage(msg);
    });
    return () => connection.disconnect();
  }, [roomId, serverUrl]); // ✅ All dependencies declared
}

现在不会在每次重新渲染聊天室组件时进行重新连接。

开源 React Hooks 库

  • ahooks 一套由阿里巴巴开源的 React Hooks 库,封装了大量好用的 Hooks。
  • react-use 一个必不可少的 React Hooks 集合。其包含了传感器、用户界面、动画效果、副作用、生命周期、状态这六大类的Hooks。
  • useHooks 一组易于理解的 React Hook集合。
  • react-recipes 一个包含流行的自定义 Hook 的 React Hooks 实用程序库。
  • Rhooks  一组基本的 React 自定义Hooks。
  • react-hanger 一组有用的 hooks,用于特定于某些基本类型的状态更改辅助函数。
  • Beautiful React Hook 一组漂亮的(希望有用的)React hooks 来加速你的组件和 hooks 开发。
  • Awesome React Hooks 一个很棒的 React Hooks 资源集合,该集合包含React Hooks教程、视频、工具,Hooks列表。其中Hooks列表中包含了众多实用的自定义Hooks。
  • SWR 一个用于获取数据的 React Hooks 库。只需一个Hook,就可以显着简化项目中的数据获取逻辑。
  • React Hook Form 一个用于表单状态管理和验证的 React Hooks (Web + React Native)。

总结

自定义 Hooks 可以帮助你迁移到更好的开发范式。通过将一些通用逻辑封装在自定义 Hooks 中,你可以使组件代码保持简洁专注于核心意图,这有助于减少重复性的代码,并使你的代码更易于维护更新,从而使你能够更快速地开发新功能。

对于 Effect 而言,这样可以使数据在 Effects 中流动的过程变得非常明确。这让你的组件能够专注于意图,而不是 Effects 的具体实现。当 React 添加新功能时,你可以删除那些 Effects 而不影响任何组件。就像设计系统一样,你可能会发现从应用程序组件中提取常见习惯用法到自定义 Hooks 中是有非常帮助的。这将使你的组件代码专注于意图,并允许你避免频繁编写原始 Effects,这也是 React 开发者所推崇的。

(학습 영상 공유: 기본 프로그래밍 영상)

위 내용은 React의 커스텀 Hook에 대한 심층적인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제