[2024.06.05] TIL 34일차

김선민·2024년 6월 5일

# useEffect

반응형 의존성 지정

  • Effect의 의존성을 "선택"할 수 없다는 점에 유의해야 한다.
  • Effect의 코드에서 사용되는 모든 반응형 값은 의존성으로 선언해야 한다.
  • Effect의 의존성 목록은 주변 코드에 의해 결정된다.
  • serverUrl 또는 roomId가 변경되면 Effect는 새 값을 사용하여 채팅에 다시 연결한다.
	 // This is a reactive value
    function ChatRoom({ roomId }) { 
	   // This is a reactive value too
      const [serverUrl, setServerUrl] = useState('https://localhost:1234'); 

      useEffect(() => {
      	 // This Effect reads these reactive values
        const connection = createConnection(serverUrl, roomId); 
        connection.connect();
        
        return () => connection.disconnect();
        
        // ✅ So you must specify them as dependencies of your Effect
      }, [serverUrl, roomId]); 
      // ...
    }

  • 반응형 값에는 props와 컴포넌트 내부에서 직접 선언된 모든 변수, 함수가 포함된다.
  • roomIdserverUrl은 반응형 값이기 때문에 의존성 목록에서 제거할 수 없다.
  • 만약 이 값을 생략하려고 할 때 린터가 React용으로 올바르게 구성되어 있다면, 린터는 이를 수정해야 하는 실수로 표시해준다.
    function ChatRoom({ roomId }) {
      const [serverUrl, setServerUrl] = useState('https://localhost:1234');

      useEffect(() => {
        const connection = createConnection(serverUrl, roomId);
        connection.connect();
        return () => connection.disconnect();
      }, []); // 🔴 React Hook useEffect has missing dependencies: 'roomId' and 'serverUrl'
      // ...
    }

  • 의존성을 제거하려면, 의존성이어야 할 필요가 없음을 린터에게 증명해야 한다.
  • 예를 들어, serverUrl을 컴포넌트 밖으로 이동시킴으로써 반응형이 아니며 리렌더링시에도
    변경되지 않음을 증명할 수 있다.
    const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore

    function ChatRoom({ roomId }) {
      useEffect(() => {
        const connection = createConnection(serverUrl, roomId);
        connection.connect();
        return () => connection.disconnect();
      }, [roomId]); // ✅ All dependencies declared
      // ...
    }
  • 이제 serverUrl은 반응형 값이 아니므로 의존성이 될 필요가 없다.
  • Effect의 코드가 반응형 값을 사용하지 않는다면 의존성 목록은 비어 있어야 한다.([]);
  • 빈 의존성이 있는 Effect는 컴포넌트의 props나 state가 변경되어도 다시 실행되지 않는다.
    const serverUrl = 'https://localhost:1234'; // Not a reactive value anymore
    const roomId = 'music'; // Not a reactive value anymore

    function ChatRoom() {
      useEffect(() => {
        const connection = createConnection(serverUrl, roomId);
        connection.connect();
        return () => connection.disconnect();
      }, []); // ✅ All dependencies declared
      // ...
    }

Pitfall | 함정

  • 기존 코드베이스가 있는 경우 이와 같이 린터를 억제하는 Effect가 있을 수 있다.
    useEffect(() => {
      // ...
      // 🔴 Avoid suppressing the linter like this:
      // eslint-ignore-next-line react-hooks/exhaustive-deps
    }, []);
  • 의존성이 코드와 일치하지 않으면 버그가 발생할 위험이 높다.
  • 린터를 억제하는 것은 곧 Effect가 의존하는 값에 대해 React에 "거짓말"을 하는 것이다.
  • 대신 의존성들이 불필요하다는 것을 증명해야 한다.

반응형 의존성 전달 예시

  • 의존성 배열 전달하기
    - 의존성을 지정하면서 Effect는 초기 렌더링 후 및 변경된 의존성으로 다시 렌더링한 후에 실행된다.
    useEffect(() => {
      // ...
    }, [a, b]); // Runs again if a or b are different


  • 아래 예시에서 serverUrlroomId는 반응형 값이므로 둘 다 의존성으로 지정해야 한다.
  • 따라서 드롭다운에서 다른 방을 선택하거나 서버 URL 입력을 수정하면 채팅이 다시 연결된다
  • 하지만 message는 Effect에서 사용되지 않으므로 메시지를 편집해도 채팅에 다시 연결되지 않는다.
// App.js
.
    import { useState, useEffect } from 'react';
    import { createConnection } from './chat.js';
.
    function ChatRoom({ roomId }) {
      const [serverUrl, setServerUrl] = useState('https://localhost:1234');
      const [message, setMessage] = useState('');
.
      useEffect(() => {
        const connection = createConnection(serverUrl, roomId);
        connection.connect();
        return () => {
          connection.disconnect();
        };
      }, [serverUrl, roomId]);
.
      return (
        <>
          <label>
            Server URL:{' '}
            <input
              value={serverUrl}
              onChange={e => setServerUrl(e.target.value)}
            />
          </label>
          <h1>Welcome to the {roomId} room!</h1>
          <label>
            Your message:{' '}
            <input value={message} onChange={e => setMessage(e.target.value)} />
          </label>
        </>
      );
    }
.
    export default function App() {
      const [show, setShow] = useState(false);
      const [roomId, setRoomId] = useState('general');
      return (
        <>
          <label>
            Choose the chat room:{' '}
            <select
              value={roomId}
              onChange={e => setRoomId(e.target.value)}

              <option value="general">general</option>
              <option value="travel">travel</option>
              <option value="music">music</option>
            </select>
            <button onClick={() => setShow(!show)}>
              {show ? 'Close chat' : 'Open chat'}
            </button>
          </label>
          {show && <hr />}
          {show && <ChatRoom roomId={roomId}/>}
        </>
      );
    }
// Chat.js
.
    export function createConnection(serverUrl, roomId) {
      // A real implementation would actually connect to the server
      return {
        connect() {
          console.log('✅ Connecting to "' + roomId + '" room at ' + serverUrl + '...');
        },
        disconnect() {
          console.log('❌ Disconnected from "' + roomId + '" room at ' + serverUrl);
        }
      };
    }












profile
웹 프론트엔드

0개의 댓글