React 공식 문서 읽어보기 (7)

Snoop So·2023년 8월 24일
0

useEffectEvent

effect 안에서 호출할 수 있으면서도, event handler와 같이 동작하는 것
특정하게 의존성을 지정하지 않아도 안에 상태가 들어있으면 해당 상태에 의해 호출되는 형식인듯 하다. 그래서 useEffect에서의 의존성을 지울 수 가 있다..?!

import { useState, useEffect } from 'react';
import { experimental_useEffectEvent as useEffectEvent } from 'react';

export default function Timer() {
  const [count, setCount] = useState(0);
  const [increment, setIncrement] = useState(1);

  const onTick = useEffectEvent(() => {
    setCount(c => c + increment);
  });

  useEffect(() => {
    const id = setInterval(() => {
      onTick();
    }, 1000);
    return () => {
      clearInterval(id);
    };
  }, []);

  return (
    <>
      <h1>
        Counter: {count}
        <button onClick={() => setCount(0)}>Reset</button>
      </h1>
      <hr />
      <p>
        Every second, increment by:
        <button disabled={increment === 0} onClick={() => {
          setIncrement(i => i - 1);
        }}></button>
        <b>{increment}</b>
        <button onClick={() => {
          setIncrement(i => i + 1);
        }}>+</button>
      </p>
    </>
  );
}

Effect 의존성 제거하기

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';

const serverUrl = 'https://localhost:1234';

function ChatRoom({ roomId }) {
  const [message, setMessage] = useState('');

  // Temporarily disable the linter to demonstrate the problem
  // eslint-disable-next-line react-hooks/exhaustive-deps
  const options = {
    serverUrl: serverUrl,
    roomId: roomId
  };

  useEffect(() => {
    const connection = createConnection(options);
    connection.connect();
    return () => connection.disconnect();
  }, [options]);

  return (
    <>
      <h1>Welcome to the {roomId} room!</h1>
      <input value={message} onChange={e => setMessage(e.target.value)} />
    </>
  );
}

export default function App() {
  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>
      </label>
      <hr />
      <ChatRoom roomId={roomId} />
    </>
  );
}

겉보기에는 문제가 없어보이는데, options 가 렌더링 될 때마다 바뀌는 dependency라서 계속 생성되고 있는 상황이다. 즉, 반응형 값이 아니다.

options를 안으로 옮겨오고, dependecy를 roomId로 바꿔주면 해결된다.

function ChatRoom({ options }) {  

const [message, setMessage] = useState('');  

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

// ...

객체를 dependency로 두면 위험하다. 이렇게 되면 부모 컴포넌트가 리렌더링할 때마다 Effect가 재실행됨. 그런데 왜 그런걸까? 앞에서 살펴봤듯이 객체는 Object.is()로 달라진 것을 식별하기 때문에 React는 렌더링 때마다 매번 달라진다고 판단할 수밖에 없음.

function ChatRoom({ options }) {  

const [message, setMessage] = useState('');  
const { roomId, serverUrl } = options;  

useEffect(() => {  
	const connection = createConnection({  
	roomId: roomId,  
	serverUrl: serverUrl  
	});  

	connection.connect();  
	return () => connection.disconnect();  
}, [roomId, serverUrl]); // ✅ All dependencies declared  

// ...

외부에서 객체 정보를 읽어 온 후에 의존성에 넣어주는 것이 안전. 함수도 동일함!

function ChatRoom({ getOptions }) {  

const [message, setMessage] = useState('');  

  

const { roomId, serverUrl } = getOptions();  

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

// ...

커스텀 훅으로 로직 재사용하기

커스텀 훅은 state 자체가 아닌 상태적인 로직(stateful logic) 을 공유함. 완전히 독립적인 상태를 가짐.

function StatusBar() {  
	const isOnline = useOnlineStatus();  
	// ...  
}  

  

function SaveButton() {  
	
	const isOnline = useOnlineStatus();  	
	// ...  

}

커스텀 훅은 컴포넌트와 함께 렌더링됨. 그래서 항상 최신 props와 state를 받음.

import { useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';
export function useChatRoom({ serverUrl, roomId }) {

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

}

커스텀 훅의 이름은 코드를 자주 작성하지 않는 사람이라도 무엇을 하고, 무엇을 취하고, 무엇을 반환하는지 짐작할 수 있을 정도로 명확해야 함

  • ✅ useData(url)
  • ✅ useImpressionLog(eventName, extraData)
  • ✅ useChatRoom(options)

외부 시스템과 동기화 할 때 전문 용어를 사용할 수 있음

  • ✅ useMediaQuery(query)
  • ✅ useSocket(url)
  • ✅ useIntersectionObserver(ref, options)

커스텀 생명주기는 사용하지 말 것. 생명주기 훅은 React 패러다임에 잘 맞지 않음.

  • 🔴 useMount(fn)
  • 🔴 useEffectOnce(fn)
  • 🔴 useUpdateEffect(fn)

Todo

  • 맨마지막 비틀거리는 움직임 구현하기

0개의 댓글

관련 채용 정보