728x90
728x90

useLayoutEffect 훅

들어가며

  • 리액트(React.js)의 @useLayoutEffect@ 훅에 대해 간단히 정리해본다.

 

useLayoutEffect 훅

개념

  • 컴포넌트가 렌더링되기 직전, 즉 브라우저가 화면을 그리기 전에 실행되는 훅
  • 화면에 변화가 나타나기 전에 어떠한 처리를 하고 싶을 때 사용한다.

 

사용 방법

import { useLayoutEffect } from 'react';

useLayoutEffect(() => {
  // 레이아웃 관련 변경, 동기적 DOM 조작 등에 사용한다.
}, [dependencies]);

 

useEffect 훅과 비교하기

항목 useEffect 훅 useLayoutEffect 훅
실행 시점 컴포넌트 렌더링 후, 브라우저가 화면을 그린 뒤 컴포넌트 렌더링 직후, 브라우저가 화면을 그리기 직전
비동기 비동기적 (렌더링 완료 후 실행됨.) 동기적 (DOM 변경 전에 실행됨.)
사용 목적 API 호출, 이벤트 리스너 등록 등 비시각적 처리 DOM 측정, 스타일 계산, 스크롤 위치 변경 등 시각적 처리
UI 깜빡임 가능성 있음 (스타일이 나중에 바뀔 수 있음) 없음 (변경이 화면 그리기 전에 적용됨)
브라우저 화면 렌더링 방해 여부 없음 있음 (렌더링 블로킹 가능성 있음.)

 

useLayoutEffect 훅을 사용하면 성능이 저하될 수 있으므로, 가능하면 useEffect 훅을 사용한다.

 

언제 @useLayoutEffect@를 써야 할까?

  • DOM 크기 측정 (@offsetWidth@, @getBoundingClientRect@ 등)
  • 스타일이나 레이아웃 조작
  • 스크롤 위치 조정
  • React Navigation 라이브러리를 이용할 때, @navigation.setOptions()@처럼 초기 렌더 타이틀이 깔끔하게 반영되어야 하는 경우 [React Native]
useLayoutEffect(() => {
  navigation.setOptions({
    title: '동적으로 설정된 제목',
  });
}, [navigation]);

 

예제 코드 비교해보기

useEffect(() => {
  console.log('useEffect 실행');
}, []);

useLayoutEffect(() => {
  console.log('useLayoutEffect 실행');
}, []);

 

⇒ ①컴포넌트 함수 실행 → ②@useLayoutEffect@ 훅 실행 → ③브라우저가 화면을 그림 → ④@useEffect@ 훅 실행

 

정리

  • @useLayoutEffect@ 훅은 컴포넌트가 렌더링되기 직전(브라우저가 화면을 그리기 전) 실행되는 훅이다.
  • 데이터 페칭, 로그 출력, 이벤트 핸들링 등의 작업을 할 때는 @useEffect@ 훅을 사용한다.
  • 레이아웃 관련 조작, DOM 크기 측정, 애니메이션 초기값을 설정할 때는 @useLayoutEffect@ 훅을 사용한다.

 

예제 코드

[React Native] 초기 렌더링 시, 스크린 타이틀이 깔끔하게 반영되도록 하기
import { useLayoutEffect } from 'react';
import { View, StyleSheet, FlatList, Text } from 'react-native';

import { MEALS, CATEGORIES } from '../data/dummy-data';

import MealItem from '../components/MealItem';

function MealsOverviewScreen({ route, navigation }) {
  const categoryId = route.params.categoryId; // 파라미터 값 가져오기

  const displayedMeals = MEALS.filter((mealItem) => {
    return mealItem.categoryIds.indexOf(categoryId) >= 0; // 데이터가 있는 항목만 필터링
  });

  // 동적으로 스크린 이름 지정하기
  useLayoutEffect(() => {
    const categoryTitle = CATEGORIES.find(
      (category) => category.id === categoryId
    ).title;

    navigation.setOptions({
      title: categoryTitle,
    });
  }, [categoryId, navigation]);

  // 아이템 렌더링
  function renderMealItem(itemData) {
    const item = itemData.item;
    const mealItemProps = {
      title: item.title,
      imageUrl: item.imageUrl,
      duration: item.duration,
      complexity: item.complexity,
      affordability: item.affordability,
    };

    return <MealItem {...mealItemProps} />;
  }

  return (
    <View style={styles.container}>
      <FlatList
        data={displayedMeals}
        keyExtractor={(item) => item.id}
        renderItem={renderMealItem}
      />
    </View>
  );
}

export default MealsOverviewScreen;

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
  },
});

 

[React] API 요청 후 데이터 표시하기
import { useState, useEffect } from 'react';

function UserList() {
  const [users, setUsers] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  // 컴포넌트 마운트 시 API 호출
  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((res) => res.json())
      .then((data) => {
        setUsers(data);
        setIsLoading(false);
      })
      .catch((error) => {
        console.error('에러 발생:', error);
        setIsLoading(false);
      });
  }, []); 

  return (
    <div>
      <h2>사용자 목록</h2>
      {isLoading ? (
        <p>로딩 중...</p>
      ) : (
        <ul>
          {users.map((user) => (
            <li key={user.id}>{user.name} ({user.email})</li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default UserList;

 

[React] @<div>@ 요소의 크기를 파악하고 레이아웃 관련 작업하기
import { useRef, useState, useLayoutEffect } from 'react';

function Box() {
  const boxRef = useRef(null);
  const [width, setWidth] = useState(0);

  useLayoutEffect(() => {
    if (boxRef.current) {
      const boxWidth = boxRef.current.getBoundingClientRect().width;
      setWidth(boxWidth); // DOM 읽고 상태 설정 (동기 작업)
    }
  }, []);

  return (
    <div>
      <div
        ref={boxRef}
        style={{
          width: '50%',
          height: '100px',
          backgroundColor: 'skyblue',
        }}
      >
        박스
      </div>
      <p>박스의 너비는 {width}px 입니다.</p>
    </div>
  );
}

export default Box;

 

참고 사이트

 

useEffect – React

The library for web and native user interfaces

ko.react.dev

 

useLayoutEffect – React

The library for web and native user interfaces

ko.react.dev

 

728x90
728x90