[React] Hooks

rin·2020년 5월 13일
1

React

목록 보기
2/16
post-thumbnail

ref. React Document - hooks

Hook의 도입

React v.16.8부터 도입된 Hook은 Class를 작성할 필요없이 상태 값과 여러 React 기능을 사용할 수 있도록 고안되었다.

Hook의 특징과 동기

✔️특징
1. 선택적 사용 : 기존 코드를 재작성할 필요없이 일부 컴포넌트 내에서 선택적으로 Hook을 사용할 수 있다.
2. 100% 호환성 : 이전 버전과 항상 100% 호환되며, 호환성을 깨뜨리는 변화가 없다
3. v16.8.0 배포버전부터 사용가능하다.

✔️동기
컴포넌트 사이에서 상태와 관련된 로직을 재사용하기 어려웠으며 재사용 가능한 액션을 붙이는 방법 또한 제공하지 않았다. 이를 가능하게 하는 패턴을 사용해야했으나, 유지보수성이 떨어졌다.
Hook을 도입하면서 이를 이용해 컴포넌트로부터 상태 관련 로직을 추상화할 수 있게 되었고 독립적인 테스트와 재사용이 가능해졌다. 즉, Hook은 계층 변화없이 상태 관련 로직을 재사용할 수 있도록 도와준다. 따라서 많은 컴포넌트들 사이에서 Hook을 공유하기가 쉬워진다.

복잡한 컴포넌트들은 이해하기 어렵다. 유지하기 힘든 상태 관련 로직들과 사이드 이펙트가 있는 컴포넌트들을 유지해야했고, 각 생명주기 메서드에서는 관련 없는 로직이 섞이곤 한다. 이런 요소들은 잠재적으로 버그를 일으키거나 무결성을 쉽게 해친다.
생명주기 메서드를 기반으로 로직을 쪼개는데 초점을 맞추지 않도록 Hook을 통해 로직에 기반을 둔 함수가 기준이 돼 컴포넌트를 나눌 수 있게 되었다.

Class는 코드의 재사용성과 구성을 어렵게 만들며, 큰 러닝커브를 가져왔고 개발자와 기계 모두를 혼란스럽게 만들었다. Hook은 Class없이 React 기능들을 사용하는 방법을 알려준다. 개념적으로 React 컴포넌트는 함수에 항상 더 가까우며 Hook은 React의 이런 특징을 보존하게 해준다.

Hook의 개요

⭐️ Hook은 함수 컴포넌트에서 React state와 생명주기 기능(lifecycle features)을 "연동 (hook into)"할 수 있게 해주는 함수이다. Hook은 Class안에서 동작하지 않으며, Class 없이 React를 사용할 수 있게 해준다. React는 내장 Hook을 몇가지 제공하며 컴포넌트 간에 상태 관련 로직을 재사용하기 위해 Hook을 직접 만드는 것도 가능하다.

Hook 사용 규칙
1. 최상위에서만 Hook을 호출해야한다. 반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하면 안된다.
2. React 함수 컴포넌트 내에서만 Hook을 호출해야한다. (+ 직접 작성한 custom Hook) 일반 JavaScript 함수에서는 Hook을 호출해서는 안된다.

State Hook 사용하기

state 추가하기

🤔 : 함수 컴포넌트는 state가 없는 컴포넌트가 아닌가요 ??????????? 그래서 Class 형식을 사용한 것 아닌가요 ???????
👉 useState Hook은 React state를 함수 안에서 사용할 수 있게 해준다!!!

import React, { useState } from 'react';
 
function Example() {
    // 새로운 state 변수의 이름은 count이며, 0으로 초기화한다.
    // 이 state는 setCount를 이용해 setting할 수 있다.
    const [count, setCount] = useState(0);
}

useState로 선언된 state 변수는 사라지지 않고 React에 의해 보존된다. useState() Hook의 인자로 넘져주는 것은 state의 초기 값이며 숫자 타입, 문자 타입, 객체 타입을 가질 수 있다. 단, n개의 state 변수를 저장하기 위해서는 useState()를 n번 호출해야한다.

import React, { useState } from 'react';
 
function Example() {
    // 여러 개의 state를 선언할 수 있다.
    const [age, setAge] = useState(42);
    const [fruit, setFruit] = useState('banana');
    const [todos, setTodos] = useState([{text: 'Learn Hooks'}]);
}
 
//위 코드는 age, fruit, todos라는 지역 변수를 가지며 '개별적'으로 갱신할 수 있다.
function handleOrangeClick(){
    setFruit('orange'); // 클래스의 this.setState({fruit: 'orange'})과 같은 효과
}

배열 구조 분해 할당을 수행한다. class의 this.state.count에 대응되는 count, this.setState에 대응되는 setCount를 값으로 가지는 배열 [count(: state 변수), setCount(: 해당 변수를 갱신할 수 있는 함수)]을 반환한다. 배열 구조 분해라는 특별한 방법으로 변수를 선언해주었으므로 [0]이나 [1]로 배열에 접근하는 것은 좋지 않을 수 있다.

컴포넌트가 다음 렌더링을 하는 동안에는 useState는 현재 state를 제공한다. 즉, React는 해당 변수를 리렌더링할 때 기억하고, 가장 최근에 갱신된 값을 제공한다.

state 가져오기, 갱신하기

// 가져오기
// state이름인 count를 직접 사용할 수 있다.
<p>You clicked {count} times</p>
 
// 갱신하기
// 기억하고 있는 count를 가져오고, 선언시에 사용한 setCount를 이용한다.
<button onClick={() => setCount(count+1)}>
    Click me
</button>

document - useState

const [state, setState] = useState(initialState);

상태를 나타내는 값과 그것을 업데이트하는 함수를 반환한다. 처음 렌더링되는 동안에 상태 state는 첫번째 argument인 initialState와 같은 값으로 초기화된다. setState 함수는 상태를 업데이트 할 때 사용된다. 새 state 값을 받아 컴포넌트의 리랜더링을 큐에 집어 넣는다.

setState(newState);

그다음 리렌더링에서 useState를 통해 반환받은 첫 번째 값(state)은 항상 갱신된 최신 state가 됩니다.

❗️NOTE
리액트는 setState 함수의 정체적이 안정적이며 리랜더링 시에도 변하지 않을 것을 보장한다. 이것이 useEffectuseCallback 의존성 목록에 이 함수(set)를 포함하지 않아도 무방한 이유이다.

setXX에는 값이 아니라 함수 또한 전달이 가능하다. 예를 들어 이전 state를 사용해 새로운 state를 계산하는 함수를 setState로 전달할 수 있다.

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

만약 업데이트 함수가 현재 상태와 정확히 동일한 값을 반환하게 된다면, 재렌더링은 일어나지 않는다.

❗️NOTE
클래스 구성 요소에 있는 setState 메서드와 달리 useState는 업데이트 개체를 자동으로 병합하지 않는다. 함수 업데이트 form을 spread 구문(...object)과 결합하여 이 동작을 복제할 수 있다.

setState(prevState => {
  // Object.assign would also work
  return {...prevState, ...updatedValues};
});

다른 옵션은 여러 하위 값을 포함하는 상태 개체를 관리하기에 더 적합한 useReducer를 사용하는 것이다.

initialState 인자는 초기 렌더링에 사용하는 state이므로 이후에는 이 값이 무시된다. 따라서 초기 state를 계산하는데 비용이 많이 든다면 초기 렌더링 시에만 실행될 "함수"로 제공할 수도 있다.

const [state, setState] = useState(() => {
  const initialState = someExpensiveComputation(props);
  return initialState;
});

State Hook을 현재 state와 동일한 값으로 갱신할 경우, React는 즉시 처리를 종료한다. (이 때 Object.is 비교 알고리즘을 사용한다.) 실행을 회피하기 전에 리액트가 특정 컴포넌트를 재렌더링하고자 할 수 있음을 기억해야한다. 일반적으론 "더 깊게" 트리에 관여하지 않겠지만, 만약 렌더링시에 큰 자원이 소모된다면 useMemo를 사용해 최적화 할 수도 있다.

Effect Hook 사용하기

effect 추가하기

데이터 가져오기, 구독 설정하기, 수동으로 리액트 컴포넌트 DOM을 수정하는 것까지 side effects이다. 즉, 리액트 class 생명주기 중 componentDidMount, componentDidUpdate, componentWillUnmount가 합쳐진 것이라고 생각하면 된다.
정리(clean-up)가 필요한 것과 그렇지 않은 것으로 나눠진다.

정리(Clean-up)를 이용하지 않는 Effects

  • 실행 이후 신경 쓸 것이 없는 경우
  • 리액트가 DOM을 업데이트한 뒤 추가로 코드를 실행할 필요가 없다.
  • 네트워크 리퀘스트, DOM 수동 조작, 로깅 등

동일한 컴포넌트를 클래스와 함수형 두 가지로 작성해보겠다.
react class

class Example extends React.Component{
    ...
    componentDidMount(){
        document.title = 'You click ${this.state.count} times';
    }
    componentDidUpdate(){
        document.title = 'You click ${this.state.count} times';
    }
    ...
}

리액트 class 컴포넌트에서 render 메서드 그 자체는 side effect를 발생시키지 않는다. 따라서 생명주기 함수를 사용하여 동일한 코드를 반복하게 된다. (DOM이 올라가면서 한 번, 상태가 변경 될 때 마다 매번 title을 변경함)

hook

import React, { useState, useEffect } from 'react';
 
function Example(){
    const [count, setCount] = useState(0);
 
    useEffect(() => {
        document.title = 'You clicked ${count} times';
    });
 
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count+1)}>
                Click me
            </button>
        </div>
    );
}

componentDidMount 혹은 componentDidUpdate와는 달리 useEffect에서 사용되는 effect는 브라우저가 화면을 업데이트하는 것을 차단하지 않아 애플리케이션의 반응성이 향상된다. 또한 대부분 effect는 동기적으로 실행될 필요가 없다. 만약 동기적 실행이 필요한 경우에는 useEffect와 동일한 API를 사용하는 useLayoutEffect라는 별도의 Hook이 존재한다.

useEffect Hook을 이용하여 리액트에게 컴포넌트가 렌더링된 후에 어떤 일을 수행(=effect 함수)해야하는지 알린다. 이 때 데이터를 가져오거나 다른 명령형 api를 불러내는 일도 할 수 있다. 리액트는 DOM "업데이트 후"에 effect 함수를 호출하는데, 첫번째 렌더링과 이후의 모든 업데이트에서 수행되므로 리액트는 effect가 수행되는 시점에 이미 DOM이 업데이트되었음을 보장한다. 리렌더링 할 때마다 모두 이전과 다른 effect로 교체하여 전달된다.

정리(Clean-up)를 이용하는 Effects

  • 외부 데이터에 구독(subscription)을 설정해야 하는 경우
  • 메모리 누수가 발생하지 않도록 정리하도록 한다.

react class

class FriendState extends React.Component{
    ...
    componentDidMount(){
        ChatAPI.subscribeToFriendStatus( //구독설정
            this.props.friend.id,
            this.handleStatusChange
        );
    }
    componentWillUnmount(){
        ChatAPI.unsubscribeFromFriendStatus( //구독해제
            this.props.friend.id,
            this.handleStatusChange
        );
    }
 
 
    handleStatusChange(){ //커스텀 함수
        this.setState({
            isOnline: status.isOnline
        });
    }
    ...
}

componentDidMount, componentWillUnmount 두 메서드 내에 개념상 똑같은 effect에 대한 코드가 있음에도 불구하고, 생명주기에 맞춰 이를 분리하게 만든다.

Hook

import React, { useState, useEffect } from 'react';
 
function FriendStatus(props) {
    const [isOnline, setIsOnline] = useState(null);
 
    useEffect(() => {
        function handleStatusChange(status) {
            setIsOnline(status.isOnline);
        }
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        //effect 이후에 어떻게 clean-up할 지 명시
        return function cleanup(){
ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    });
 
    if (isOnline === null) {
        return 'Loading...';
    }
    return isOnline ? 'Online' : 'Offline';
}

구독의 추가와 제거를 위한 코드는 결함도가 높기 때문에 useEffect는 이를 함께 다루도록 고안되었다. → effect가 cleanup() 함수를 반환하면 리액트는 그 함수를 정리가 필요한 때에 실행시킨다.

🤔 effect에서 함수를 반환하는 이유는?
effect를 위한 "추가적인" clean-up 메커니즘이기 때문이다. 모든 effect는 clean-up을 위한 함수를 반환할 수 있지만 clean-up이 필요없는 경우에는 어떤것도 반환하지 않는다. 또한 effect에서 반드시 named function을 반환해야하는 것은 아니며 화살표 함수를 반환해도 무관하며, 함수의 이름도 목적을 분명히 하기위해 cleanup이라고 명명할 뿐 다르게 붙여도 무방하다.

리액트는 컴포넌트가 마운트 해제되는 때에 clean-up을 수행하도록 되어있지만, ⭐️effect는 한번이 아니라 렌더링이 실행되는 때마다 수행되고, 다음 차례의 effect를 실행하기 전에 이전 렌더링에서 파생된 effect를 clean-up한다.

document - useEffect

useEffect(didUpdate);

어떤 effect를 발생시키는 일반 함수/명령형 함수를 인자로 받는다. side effect는 함수 컴포넌트 내의 본문 안에서 허용되지 않으므로 useEffect를 사용하도록 한다. useEffect에 전달된 함수는 화면에 렌더링이 완료된 후에 수행되는게 기본이지만, 어떤 값이 변경된 경우에만 실행되게 할 수도 있다.

effect는 종종 컴포넌트가 화면에서 제거될 때 사라져야하는 리소스를 만들곤 한다. 가령 구독이나 타이머 ID 같은 것들이다. 따라서 이런 것들을 제거하기 위해서 useEffect로 전달된 "함수"clean-up 함수를 반환 할 수 있다. 예를 들어 구독을 생성하는 경우에는 아래와 같다.

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

clean-up 함수는 메모리 누수 방지를 위해 UI에서 컴포넌트를 제거하기 전에 수행된다. 만약 컴포넌트가 제거되기 전에 여러번 재렌더링되면 다음 effect의 수행 전(재렌더링 후)에 이전 effect를 clean-up한다.

useEffect로 전달된 함수는 렌더링이 끝날 때 까지 이벤트가 지연되는데 이는 부작용을 가져올 수 있다. 예를 들어 사용자에게 노출되는 DOM 변경은 사용자가 노출된 내용이 불일치하는 경험을 하지 않도록 다음 화면을 다 그리기 이전에 동기화 되어야한다. 이런 종류의 effect를 위해 리액트는 useLayoutEffect라는 추가적인 Hook을 제공하는데, 이것은 useEffect와 동일한 시그니처를 가지고 있고 수행될 때에만 차이가 난다.

useEffect는 브라우저 화면이 다 그려질 때까지 지연되지만, 다음 어떤 새로운 렌더링이 발생하기 이전에 발생하는 것도 보장한다. React는 새로 갱신을 시작하기 전에 이전 렌더링을 완료하도록 한다.

effect는 기본적으로 의존성이 있는 값들 중 하나만 변경되도 재생성된다. 이는 일부 경우에서는 과도한 작업일 수 있다. 즉, source props가 변경될 때만 effect가 필요하다면 매번 일어나는 갱신마다 새로운 subscription을 생성할 필요가 없단 소리이다. 이를 수행하기 위해선 useEffect의 두 번째 인자에 effect가 종속되어 있는 값의 배열을 전달하면 된다. 아래의 예에선 props.source가 변경될 때에만 subscription이 재생산 될 것이다.

useEffect(
  () => {
    const subscription = props.source.subscribe();
    return () => {
      subscription.unsubscribe();
    };
  },
  [props.source],
);

❗️NOTE
이를 사용할 경우엔 배열이 시간이 지남에 따라 변경되고, effect에 사용되는 컴포넌트 번위의 모든 값들(props나 state)을 포함하고 있는지 확인해야한다. 그렇지 않으면 이전 렌더링에서 설정된 "오래된" 값을 참조할 수도 있다. how to deal with functionsarray values change too often에서 자세히 알아보라.

만약 effect를 mount/unmount 시에만 실행하고 싶으면 빈 배열 []을 전달하면 된다. 이는 effect가 props나 state에서 가져온 어떤 값에도 의존하지 않으므로 다시 실행할 필요가 전혀 없단 것을 리액트에게 알린다. 특별한 경우로 간주하기보단, 의존성이 있는 값에 대한 배열이 항상 어떻게 동작하는지 직접적으로 보여주는 것이다.

빈 배열을 전달한다면 effect 내의 props와 state는 항상 "초깃값"을 유지한다. 이것이 componentDidMountcomponentWillUnmount 개념과 비슷하게 느껴지겠지만, effect가 너무 자주 리렌더링 되는 것을 피하기 위한 일반적인 더 나은 해결책이 있다. 또한 브라우저에 모두 렌더링 될 때까지 리액트는 useEffect의 수행을 지연하므로 다른 작업의 수행이 문제 되지 않음을 잊으면 안된다.

eslint-plugin-react-hooks 패키지의 exhaustive-deps 규칙을 사용하는 것을 권장한다. 이는 의존성이 바르지 않게 정의되었으면 경고하고 수정하도록 알려준다.

이 두 번째 인자로 전달되는 배열은 effect 함수의 인자로 전달되지는 않는다.

effect를 이용하는 팁

관심사를 구분하고자한다면 Multiple Effect를 사용한다. state Hook을 여러 번 사용할 수 있는 것처럼 effect 또한 여러 번 사용할 수 있으므로 이를 이용하여 서로 관련 없는 로직들을 갈라놓을 수 있다.

function FriendStatusWithCounter(props){
    const [count, SetCount] = useState(0);
    useEffect(() => {
        document.title = 'You clicked ${count} times';
    });
 
 
    const [isOnline, setIsOnlile] = useState(null);
    useEffect(() => {
        function handleStatusChange(status) {
            setInOnline(status.isOnline);
        }
 
 
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => { // 익명 함수로 리턴
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    }, [props.friend.id]); //props.friend.id가 바뀔 때만 재구독한다.
    ...
}

눈을 크게 뜨고 뭐가 달라졌는지 찾아보자. 우선 "익명 함수"로 clean-up을 수행하도록 하였다. 그리고, useEffect에 첫번째 파라미터인 함수 외에 두번째 파라미터([props.friend.id])가 추가되어 있다. 이런 경우, props.friend.id가 변경된 경우에만 useEffect가 작동한다. 이와 같이 생명 주기 메서드에 따라서가 아닌 코드가 무엇을 하는지에 따라 나눌 수 있다.

❗️NOTE
useEffect의 두번째 인자

  • 없는 경우 : 상태값이 변경돼 DOM이 (재)렌더링 될 때 마다 모두 실행된다.
  • 빈 배열[]일 경우 : 마운트와 마운트 해제 시에 딱 한 번 씩, 총 2번만 실행된다.
  • 값이 존재하는 경우 : 이전 값과 비교해 (배열에 포함된 property 중) 하나라도 다를 경우 실행된다.

리액트는 다음처럼 컴포넌트에 사용된 모든 effect를 지정된 순서에 맞춰 적용한다.

/*** 실행 예시 **/

// {friend: {id: 100}} state를 사용하여 마운트
ChatAPI.subscribeToFriendStatus(100, handleStatusChange); //첫번째 effect가 작동
 
 
// {friend: {id: 200}} state를 사용하여 업데이트
ChatAPI.unsubscribeToFriendStatus(100, handleStatusChange); //이전의 effect를 clean-up
ChatAPI.subscribeToFriendStatus(200, handleStatusChange); //다음 effect가 작동
 
// {friend: {id: 300}} state를 사용하여 업데이트
ChatAPI.unsubscribeToFriendStatus(200, handleStatusChange); //이전의 effect를 clean-up
ChatAPI.subscribeToFriendStatus(300, handleStatusChange); //다음 effect가 작동
 
// 마운트를 해제한다.
ChatAPI.unsubscribeToFriendStatus(300, handleStatusChange); //마지막 effect를 clean-up

✏️ Effect를 건너뛰어 성능 최적화하기
모든 렌더링 이후에 effect를 clean-up하거나, 적용하는 것이 때때로 성능 저하를 방생시키는 경우도 있다.
class component의 경우, componentDidUpdate에서 prevProps나 prevState와의 비교를 통해 이러한 문제를 해결할 수 있다.

componentDidUpdate(prevProps, prevState){
  if (prevState.count !== this.state.count){
    document.title = 'You clicked ${this.state.count} times';
  }
}

useEffect Hook API에도 이미 이런 기능이 내제돼있다. 특정 값들이 리렌더링 시에 변경되지 않는다면 리액트로 하여금 effect를 건너뛰도록 할 수 있는데 useEffect의 선택적 인수인 두 번째 인수(= 특정 값)로 배열을 넘김으로써 가능하다.

useEffect(() => {
  document.title = 'You clicked ${count} times';
}, [count]); //count가 바뀔 때만 effect를 재실행한다.

배열 내에 여러개의 값이 있다면 그 중 "단 하나만 다를지라도" 리액트는 effect를 재실행한다. clean-up을 사용하는 effect의 경우에도 동일하게 작용한다. 배열이 컴포넌트 범위 내에서 바뀌는 값들과 effect에 의해 사용되는 값들을 모두 포함한다.

effect를 실행하고 이를 clean-up하는 과정을 마운트와 마운트 해제 시에 딱 한 번씩만 실행하고 싶다면, 빈 배열을 두 번째 인수로 넘기면 된다. 이는 해당 effect가 prop이나 state의 값에 의존하지 않으며, 재실행될 필요가 없음을 명시한다.

Hook의 규칙

최상위에서만 Hook을 호출해야 한다.

  • 반복문, 조건문, 중첩된 함수 내에서 Hook을 호출하면 안된다.
    만약 조건부로 effect를 실행하기를 원한다면, 조건문을 Hook 내부에 넣을 수 있다.
  • 항상 React 함수의 최상위에서 호출해야한다.
  • 컴포넌트가 렌더링 될 때마다 항상 동일한 순서로 Hook이 호출되는 것을 보장함으로써 useState와 useEffect가 여러번 호출되는 중에도 Hook의 상태를 올바르게 유지할 수 있도록 해준다.

오직 React 함수 내에서 Hook을 호출해야 한다.

  • 일반적인 JavaScript 함수에서 호출하면 안된다.
  • React 함수 컴포넌트에서 Hook을 호출한다.
  • Custom Hook에서 Hook을 호출한다.

Custom Hook

만들기

16.8 버전 이상에서만 사용 가능하다. "두 JavaScript 함수간에 로직을 공유하려면 이를 세 번째 함수로 추출하자!"는 것이 목표이다. Component와 Hook은 모두 Function이므로 문제없이 작동시킬 수 있다.

📌 사용자 정의 hook은 'use'로 이름이 시작하고, 다른 Hook을 호출할 수 있는 JS function이다.
📌 사용자 정의 hook의 최상위 레벨에서만 다른 hooks를 호출해야한다.

아래 useFriendStatus 함수는 컴포넌트가 아니라(대문자로 시작해야한다. Use는 왠만하면 피하도록 한다.) customHook이다.

import {useState, useEffect} from 'react';
 
function useFriendStatus(friendID) {
    const [isOnline, setIsOnline] = useState(null);
     
    useEffect(() => {
        function handleStatusChange(status){
            setIsOnline(status.isOnline);
        }
 
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
        return () => {
            ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
        };
    });
     
    return isOnline;
}

사용하기

function FriendStatus(props) {
    const isOnline = useFriendStatus(props.friend.id);
     
    if (isOnline === null){
        return 'Loading...';
    }
    return isOnline ? 'Online' : 'Offline';
}
 
function FriendListItem(props) {
    const isOnline = useFriendStatus(props.friend.id);
     
    return (
        <li style={{ color: isOnline ? 'green' : 'black' }}>
            {props.friend.name}
        </li>
    );
}

custom hook은 상태 설정 로직을 재사용하는 메커니즘이지만, 내부의 모든 state와 effect는 독립적이다. Hook에 대한 각 호출은 각기 다른 state를 얻는다.

Hook 사이에 상태 전달

Hook은 함수이므로 정보를 전달할 수 있다. useState Hook 호출은 recipient ID 상태 변수의 최신 값을 제공하므로 useFriendStatus Hook에 인수로 전달할 수 있다.

profile
🌱 😈💻 🌱

0개의 댓글