React Hooks의 모범 사례
게시 됨: 2022-03-10 React Hooks는 React 16.8에 새로 추가되어 class
구성 요소를 작성하지 않고도 상태 및 기타 React 기능을 사용할 수 있습니다. 다시 말해, Hooks는 함수 구성 요소에서 React 상태 및 수명 주기 기능을 "연결"할 수 있는 함수입니다. ( class
컴포넌트 내에서는 작동하지 않습니다.)
React는 useState
와 같은 몇 가지 내장 Hook을 제공합니다. 다른 구성 요소 간에 상태 저장 동작을 재사용하기 위해 고유한 Hook을 만들 수도 있습니다. 아래 예는 상태가 useState()
후크를 사용하여 관리되는 카운터를 보여줍니다. 버튼을 클릭할 때마다 setCount()
를 사용하여 count
값을 1
만큼 업데이트합니다.
이 예에서는 값이 0
인 카운터를 렌더링합니다. 버튼을 클릭하면 값이 1
증가합니다. 구성 요소의 초기 값은 useState
를 사용하여 정의됩니다.
const [count, setCount] = useState(0)
보시다시피 0
으로 설정했습니다. 그런 다음 값을 증가시키려는 경우 onClick()
메서드를 사용하여 setCount
를 호출합니다.
<button onClick={() => setCount(count + 1)}> Click me </button>
React Hooks가 출시되기 전에 이 예제에서는 class
구성 요소를 사용해야 했기 때문에 더 많은 코드 줄을 사용했을 것입니다.
React Hooks의 규칙
베스트 프랙티스에 대해 자세히 알아보기 전에 이 기사에서 제시하는 프랙티스의 기본 개념이기도 한 React Hooks의 규칙을 이해해야 합니다.
React Hooks는 JavaScript 함수이지만 사용할 때 두 가지 규칙을 따라야 합니다.
- 최상위 수준에서 Hooks를 호출합니다.
- React 컴포넌트에서만 Hooks를 호출하세요.
참고 : 이 두 규칙은 JavaScript 자체의 일부가 아니라 React Hooks에 도입되었습니다.
이 규칙을 더 자세히 살펴보겠습니다.
최상위 수준에서 통화 후크
루프, 조건 또는 중첩 함수 내에서 Hooks를 호출하지 마십시오. 항상 React 함수의 최상위 수준에서 Hooks를 사용하세요. 이 규칙을 따르면 구성 요소가 렌더링될 때마다 후크가 동일한 순서로 호출됩니다. 이것이 React가 여러 useState
및 useEffect
호출 사이에 Hooks의 상태를 올바르게 보존할 수 있도록 하는 것입니다.
두 가지 상태가 있는 Form
구성 요소를 만들어 보겠습니다.
-
accountName
-
accountDetail
이러한 상태는 기본값을 useEffect
후크를 사용하여 브라우저의 로컬 저장소나 문서 제목에 상태를 유지합니다.
이제 이 구성 요소는 useState
와 useEffect
의 여러 호출 간에 동일하게 유지되는 경우 해당 상태를 성공적으로 관리할 수 있습니다.
function Form() { // 1. Use the accountName state variable const [accountName, setAccountName] = useState('David'); // 2. Use an effect for persisting the form useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); // 3. Use the accountDetail state variable const [accountDetail, setAccountDetail] = useState('Active'); // 4. Use an effect for updating the title useEffect(function updateStatus() { document.title = accountName + ' ' + accountDetail; }); // ... }
Hooks의 순서가 변경되면(루프에서 호출되거나 조건문에서 호출될 때 가능할 수 있음) React는 구성 요소의 상태를 보존하는 방법을 파악하는 데 어려움을 겪을 것입니다.
// ------------ useState('David') // 1. Initialize the accountName state variable with 'David' useEffect(persistForm) // 2. Add an effect for persisting the form useState('Active') // 3. Initialize the accountdetail state variable with 'Active' useEffect(updateStatus) // 4. Add an effect for updating the status // ------------- // Second render // ------------- useState('David') // 1. Read the accountName state variable (argument is ignored) useEffect(persistForm) // 2. Replace the effect for persisting the form useState('Active') // 3. Read the accountDetail state variable (argument is ignored) useEffect(updateStatus) // 4. Replace the effect for updating the status // ...
이것이 React가 후크를 호출하기 위해 따르는 순서입니다. 순서가 동일하게 유지되므로 구성 요소의 상태를 유지할 수 있습니다. 하지만 조건 안에 Hook 호출을 넣으면 어떻게 될까요?
// We're breaking the first rule by using a Hook in a condition if (accountName !== '') { useEffect(function persistForm() { localStorage.setItem('formData', accountName); }); }
accountName !== ''
조건은 첫 번째 렌더링에서 true
이므로 이 Hook을 실행합니다. 그러나 다음 렌더에서 사용자는 양식을 지우고 조건을 false
로 만들 수 있습니다. 이제 렌더링하는 동안 이 Hook을 건너뛰므로 Hook 호출의 순서가 달라집니다.
useState('David') // 1. Read the accountName state variable (argument is ignored) // useEffect(persistForm) // This Hook was skipped! useState('Active') // 2 (but was 3). Fail to read the accountDetails state variable useEffect(updateStatus) // 3 (but was 4). Fail to replace the effect
React는 두 번째 useState
Hook 호출에 대해 무엇을 반환할지 모릅니다. React는 이 컴포넌트의 두 번째 Hook 호출이 이전 렌더링과 마찬가지로 persistForm
효과에 해당할 것으로 예상했지만 더 이상 그렇지 않습니다. 그 시점부터 건너뛴 모든 다음 Hook
호출도 하나씩 이동하여 버그가 발생합니다.
이것이 Hooks가 컴포넌트의 최상위 레벨에서 호출되어야 하는 이유입니다. 조건부로 효과를 실행하려면 해당 조건을 Hook 내부 에 넣을 수 있습니다.
참고 : 이 주제에 대한 자세한 내용은 React Hook 문서를 확인하세요.
React 구성 요소에서만 호출 후크
일반 JavaScript 함수에서 Hooks를 호출하지 마십시오. 대신 React 함수 구성 요소에서 Hooks를 호출할 수 있습니다. 아래에서 JavaScript 함수와 React 구성 요소의 차이점을 살펴보겠습니다.
자바스크립트 기능
import { useState } = "react"; function toCelsius(fahrenheit) { const [name, setName] = useState("David"); return (5/9) * (fahrenheit-32); } document.getElementById("demo").innerHTML = toCelsius;
여기에서 React 패키지에서 useState
후크를 가져온 다음 함수를 선언했습니다. 그러나 이것은 React 구성 요소가 아니기 때문에 유효하지 않습니다.
반응 기능
import React, { useState} from "react"; import ReactDOM from "react-dom"; function Account(props) { const [name, setName] = useState("David"); return <p>Hello, {name}! The price is <b>{props.total}</b> and the total amount is <b>{props.amount}</b></p> } ReactDom.render( <Account total={20} amount={5000} />, document.getElementById('root') );
둘 다 몸체가 비슷해 보이지만 후자는 React를 파일로 가져올 때 구성 요소가 됩니다. 이것이 우리가 내부에서 JSX 및 React 후크와 같은 것을 사용할 수 있게 해주는 것입니다.
React를 가져오지 않고 선호하는 Hook을 가져오면(일반 기능이 됨) Hook은 React 구성 요소에서만 액세스할 수 있으므로 가져온 Hook을 사용할 수 없습니다.
사용자 정의 후크에서 후크 호출
사용자 정의 Hook은 이름이 use
로 시작하고 다른 Hook을 호출할 수 있는 JavaScript 함수입니다. 예를 들어 useUserName
은 useState
및 useEffect
후크를 호출하는 사용자 정의 후크 아래에서 사용됩니다. API에서 데이터를 가져오고 데이터를 반복하며 수신한 특정 사용자 이름이 API 데이터에 있는 경우 setIsPresent()
를 호출합니다.
export default function useUserName(userName) { const [isPresent, setIsPresent] = useState(false); useEffect(() => { const data = MockedApi.fetchData(); data.then((res) => { res.forEach((e) => { if (e.name === userName) { setIsPresent(true); } }); }); }); return isPresent; }
그런 다음 애플리케이션에서 필요한 다른 위치에서 이 후크의 기능을 재사용할 수 있습니다. 이러한 장소에서는 필요할 때를 제외하고 더 이상 useState
또는 useEffect
를 호출할 필요가 없습니다.
이 규칙을 따르면 구성 요소의 모든 상태 저장 논리가 소스 코드에서 명확하게 표시됩니다.
ESLint 플러그인
eslint eslint-plugin-react-hooks
플러그인은 위의 규칙을 적용합니다. 이것은 프로젝트에서 작업할 때 규칙을 적용하는 데 유용합니다. 프로젝트 작업, 특히 다른 사람들과 작업할 때 이 플러그인을 사용하는 것이 좋습니다. 시도하고 싶다면 이 플러그인을 프로젝트에 추가할 수 있습니다.
// Your ESLint configuration { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies } }
이 플러그인은 Create React App에 기본적으로 포함되어 있습니다. 따라서 Create-React-App을 사용하여 React 애플리케이션을 부트스트랩하는 경우 추가할 필요가 없습니다.
후크 생각
몇 가지 Hook 모범 사례를 살펴보기 전에 class
구성 요소와 기능 구성 요소(Hook 사용)에 대해 간략히 살펴보겠습니다.
React에서 컴포넌트를 정의하는 가장 간단한 방법은 React 요소를 반환하는 JavaScript 함수를 작성하는 것입니다.
function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
Welcome
구성 요소는 데이터를 포함하고 React 요소를 반환하는 개체인 props
를 허용합니다. 그런 다음 이 구성 요소를 가져오고 다른 구성 요소에서 렌더링할 수 있습니다.
class
구성 요소는 기본적으로 클래스 구성 요소와 관련된 모든 것이 그 안에 존재한다는 것을 의미하는 캡슐화 라는 프로그래밍 방법론을 사용합니다. 수명 주기 메서드( constructors
, componentDidMount()
, render
등)는 구성 요소에 예측 가능한 구조를 제공합니다.
캡슐화 는 OOP (객체 지향 프로그래밍)의 기본 중 하나입니다. 이는 해당 데이터에 대해 작동하는 메서드 내의 데이터 번들을 의미하며, 클래스 내부의 구조화된 데이터 개체의 값이나 상태를 숨기는 데 사용되어 권한이 없는 당사자의 직접 액세스를 방지합니다.
Hooks를 사용하면 구성 요소의 구성이 수명 주기 Hooks의 조합에서 마지막에 일부 렌더링이 있는 기능으로 변경됩니다.
기능 구성 요소
아래 예제는 사용자 정의 Hook이 기능 구성요소에서 어떻게 사용될 수 있는지 보여줍니다(본문이 무엇인지 보여주지 않고). 그러나 그것이 하거나 할 수 있는 것은 제한되지 않습니다. 상태 변수를 인스턴스화하고, 컨텍스트를 사용하고, 구성 요소를 다양한 부작용에 구독하거나 사용자 정의 후크를 사용하는 경우 위의 모든 것일 수 있습니다!
function { useHook{...}; useHook{...}; useHook{...}; return (
...); }
클래스 구성 요소
class
구성 요소를 사용하려면 React.Component
에서 확장하고 React 요소를 반환하는 render
함수를 만들어야 합니다. 여기에는 더 많은 코드가 필요하지만 몇 가지 이점도 있습니다.
class { constructor(props) {...} componentDidMount() {...} componentWillUnmount() {...} render() {...} }
React에서 기능적 구성 요소를 사용하면 얻을 수 있는 몇 가지 이점이 있습니다.
- 구성 요소에서
setState()
에 액세스할 수 없는 경우 구성 요소의 상태에 대해 더 많이 생각할 필요가 있기 때문에 컨테이너와 프리젠테이션 구성 요소를 분리하는 것이 더 쉬워질 것입니다. - 기능 구성 요소는 상태 또는 수명 주기 후크가 없는 일반 JavaScript 함수이기 때문에 읽고 테스트하기가 훨씬 쉽습니다 .
- 더 적은 코드로 끝납니다.
- React 팀은 향후 React 버전에서 기능적 구성 요소에 대한 성능 향상이 있을 수 있다고 언급했습니다.
이것은 React Hooks를 사용할 때 첫 번째 모범 사례로 이어집니다.
후크 모범 사례
1. 후크 단순화
React Hooks를 단순하게 유지하면 전체 수명 동안 구성 요소에서 진행되는 작업을 효과적으로 제어하고 조작할 수 있습니다. 가능한 한 커스텀 Hooks를 작성하지 마세요 . 고유한 후크를 만드는 대신 useState useState()
또는 useEffect()
를 인라인할 수 있습니다.
기능과 관련된 많은 사용자 정의 Hooks를 사용하고 있는 자신을 발견했다면, 이들에 대한 래퍼 역할을 하는 사용자 정의 Hook을 생성할 수 있습니다. 아래에서 후크가 있는 두 가지 다른 기능적 구성 요소를 살펴보겠습니다.
기능 구성 요소 v1
function { useHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
기능 구성 요소 v2
function { useCustomHook(...); useHook(...); useHook(...); return( <div>...</div> ); }
v2는 후크를 단순하게 유지하고 그에 따라 다른 모든 useHook
가 인라인되기 때문에 더 나은 버전입니다. 이를 통해 다양한 구성 요소에서 재사용할 수 있는 기능을 만들 수 있으며 구성 요소를 효과적으로 제어하고 조작할 수 있는 더 많은 권한을 얻을 수 있습니다. 구성 요소에 Hook이 흩어져 있는 v1을 채택하는 대신 디버깅을 쉽게 하고 코드를 더 깔끔하게 만드는 v2를 사용해야 합니다.
2. 후크 구성 및 구성
React Hooks의 장점 중 하나는 읽기 쉬운 코드를 적게 작성할 수 있다는 것입니다. 어떤 경우에는 useEffect()
및 useState()
)의 양이 여전히 혼란스러울 수 있습니다. 구성 요소를 조직화하면 가독성을 높이고 구성 요소의 흐름을 일관되고 예측 가능하게 유지하는 데 도움이 됩니다. 사용자 정의 Hook이 너무 복잡하면 항상 하위 사용자 정의 Hook으로 나눌 수 있습니다. 구성 요소의 논리를 사용자 정의 Hooks로 추출하여 코드를 읽을 수 있도록 합니다.
3. React Hooks 스니펫 사용
React Hooks Snippets는 React Hooks를 더 쉽고 빠르게 만드는 Visual Studio Code 확장입니다. 현재 5개의 후크가 지원됩니다.
-
useState()
-
useEffect()
-
useContext()
-
useCallback()
-
useMemo()
다른 스니펫도 추가되었습니다. 나는 이 Hooks로 작업을 시도했고 그것들로 작업하는 동안 개인적으로 사용한 베스트 프랙티스 중 하나였습니다.
프로젝트에 React Hooks 스니펫을 추가하는 방법에는 두 가지가 있습니다.
- 명령
VS Code 빠른 열기( Ctrl + P )를 시작하고ext install ALDuncanson.react-hooks-snippets
를 붙여넣고 Enter 키를 누릅니다. - 확장 마켓플레이스
'VS Code Extension Marketplace'( Ctrl + Shift + X )를 실행하고 'React Hook Snippets'를 검색합니다. 그런 다음 'Alduncanson' 아이콘을 찾으십시오.
첫 번째 단편을 추천합니다. 여기에서 스니펫에 대해 자세히 알아보거나 여기에서 최신 Hooks 스니펫을 확인하세요.
4. Hooks 규칙 고려
React Hooks로 작업할 때 앞서 배운 Hooks의 두 가지 규칙을 항상 고려하도록 노력하십시오.
- 최상위 레벨에서만 Hooks를 호출하십시오. 루프, 조건 또는 중첩 함수 내에서 Hooks를 호출하지 마십시오.
- 항상 React 함수 구성 요소 또는 사용자 정의 Hook에서 Hook을 호출하고 일반 JavaScript 함수에서 Hook을 호출하지 마십시오.
eslint-plugin-react-hooks
플러그인은 이 두 가지 규칙을 적용합니다. 원하는 경우 이 플러그인을 프로젝트에 추가할 수 있습니다. 위의 후크 규칙 섹션에서 설명한 대로입니다.
Hooks는 아직 비교적 새롭기 때문에 모범 사례가 완전히 해결되지 않았습니다. 따라서 초기 기술을 채택할 때 취하는 예방 조치를 취해야 합니다. 이를 염두에 두고 Hooks는 React의 미래를 위한 방법입니다.
결론
이 튜토리얼을 즐겼기를 바랍니다. React Hooks의 가장 중요한 두 가지 규칙과 Hooks에서 효과적으로 생각하는 방법을 배웠습니다. Hooks를 올바르고 효과적인 방식으로 작성하는 데 있어 기능적 구성 요소와 몇 가지 모범 사례를 살펴보았습니다. 규칙은 간단하지만 규칙을 작성할 때 지침이 되는 나침반으로 만드는 것이 중요합니다. 잊어버리기 쉽다면 ESLint 플러그인을 사용하여 적용할 수 있습니다.
다음 React 프로젝트에서 여기에서 배운 모든 교훈을 얻으시기 바랍니다. 행운을 빕니다!
자원
- "Hook 소개", React Docs
- "React의 기능적 대 클래스 구성 요소", David Joch, Medium
- "유해한 것으로 간주되는 믹신", Dan Abramov, React 블로그
- "React Hooks: 모범 사례와 사고 방식의 변화", Bryan Manuele, Medium
- Anthony Davis, Visual Code Marketplace "React Hooks Snippets For VS Code"