[React] React Hooks

Haeseo Lee·2023년 4월 7일
0

React

목록 보기
8/16

react hook

  • 함수형 컴포넌트에서도 react state생명주기 기능을 활용할 수 있도록 한 것
  • 함수형 컴포넌트도 클래스형 컴포넌트처럼 사용할 수 있게 하기 위함
    (함수형 컴포넌트는 클래스와 다르게 모듈로 활용하기 쉬움)
  • 조건문, 반복문 등을 사용하고 싶다면 반드시 hook 내부에서 사용하기

state

  • React에서 사용자의 반응에 따라, 렌더링을 하기 위해 사용되는 트리거 역할을 하는 변수
  • state의 변경 → 리렌더링

useState (<-> useLayoutEffect: 동기방식)

  • 함수형 컴포넌트에서 가변적인 상태를 관리할 수 있게 해줌
  • 상태값, 상태를 설정하는 함수
  • 하나의 useState 함수는 하나의 상태 값만 관리할 수 있음
  • 상태의 초기값을 인자로 넣어줌
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = **useState**(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

※ 같은 state 여러 번 업데이트하기

state가 변경되어 컴포넌트가 리렌더링될 때는 state가 전부 다 업데이트 될 때까지 기다렸다가 한 번에 리렌더링한다. 따라서 아래와 같은 방식으로 하면 number는 4가 아니라 2가 된다

const [number, setNumber] = useState(1);

const func = () => {
	setNumber(number*2);
	setNumber(number*2); 
}

의도대로 값을 바꾸고 싶으면 setNumber에 값이 아니라 함수를 넘겨준다

const func = () => {
	setNumber((prevState) => {
		return prevState*2;
	});
	setNumber((prevState) => prevState*2);
	//둘 다 같은 의미
}

useEffect

  • react 컴포넌트가 렌더링될 때마다 처리할 작업의 코드를 수행하는 hook
  • 클래스형 컴포넌트의 componentDidMount + componentDidUpdate와 유사 (clean-up을 수행한다면 componentWillUnmount 도 추가된 형태)
  • 기본적으로 첫번째 렌더링과 이후의 모든 업데이트에서 수행
  • 파라미터
    • 렌더링 이후 수행할 함수
    • deps: 동작 시점을 정할 수 있는 배열
      • 없음: 리렌더링할때마다 실행
      • 빈 배열([]): 컴포넌트가 처음 렌더링 될 때만 실행
      • 특정 값: 그 값이 변경될 경우에만 실행
function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);
}
  • effect에 정리(clean-up)가 필요할 경우 함수를 반환
    • Component의 unmount이전 / update직전에 수행할 함수
      • e.g. 타이머 제거, 서비스 구독 해지 등등
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);
 
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

컴포넌트가 마운트 해제될 때, 다음 차례의 effect를 실행하기 전에 이전의 렌더링에서 파생된 effect 또한 정리

useRef

  • 컴포넌트나 HTML 요소를 레퍼런스로 관리할 수 있음
  • current 속성을 가지고 있는 객체 반환
  • current 값이 바뀌어도 재렌더링 X, 재렌더링 시에도 current 값 없어지지 않음 ⇒ 컴포넌트가 unmount 되기 전까지는 계속 유지
import React, { useState, useRef } from 'react';

const App = () => {
	const [count, setCount] = useState(0);
	const countRef = useRef(0);

	const increaseCountState = () => {
		setCount(count + 1):;
	};

	const increaseCountRef () => {
		countRef.current = countRef.current + 1;
	};

	return (
		<div>
			<p>State: {count}</p>
			<p>Ref: {countRef.current}</p>
			<button onClick={increaseCountState}>State 증가</button>
			<button onClick={increaseCountRef}>Ref 증가</button>		
		</div>
	);
}
  • 활용
    • 값을 유지하고 싶을 때
    • 자주 바뀌는 값을 저장할 때
    • DOM 요소 접근할 때 (getElementByIdquerySelector와 유사)
      • e.g. 포커스를 주고 싶을 때, 특정 엘리먼트의 크기를 가져올 때
import { useRef, useEffect } from "react";

function App() {
  const inputRef = useRef();

  function focus() {
    inputRef.current.focus();
    console.log(inputRef.current);
  }

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="아이디 또는 이메일" />
      <button>Login</button>
      <br />
      <button onClick={focus}>focus</button>
    </div>
  );
}

export default App;

(focus버튼을 클릭하면 input창이 focus됨)

useContext

  • 다른 레벨의 컴포넌트에게 전역적인 데이터를 공유하기 위함
    • 앱 규모가 커질수록 일일히 props로 전달하기 어려워짐

useReducer

  • useState()는 컴포넌트 내부에서 상태 로직 처리
  • useReducer()는 컴포넌트의 상태 업데이트 로직을 컴포넌트에서 분리
  • reducer
    • 현재 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수

      function reducer(state, action) {
        return { ... }; // 불변성을 지키면서 업데이트한 새로운 상태를 반환합니다
      }
  • action
    • 상태를 업데이트할 방식을 지정하는 객체
{
  type: 'DECREMENT'
},
{
  type: 'ADD_TODO',
  todo: {
    id: 1,
    text: 'useReducer 배우기',
    done: false,
  }
}
  • 활용 예시
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { value: state.value + 1 };
    case 'DECREMENT':
      return { value: state.value - 1 };
    default:
      return state;
  }
}

const Counter = () => {
  const [state, dispatch] = useReducer(reducer, { value: 0 });

  return (
    <div>
      <p>
        현재 카운터 값은 <b>{state.value}</b> 입니다.
      </p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>+1</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>-1</button>
    </div>
  );
};

export default Counter;

컴포넌트 최적화

  • 함수형 컴포넌트를 렌더링한다는 것은 그 함수를 호출한다는 것
  • 따라서 호출 때마다 모든 내부 변수가 초기화됨 ⇒ 렌더링 전 변수 렌더링 후 변수
  • 컴포넌트 최적화 방법
    • useMemo (결과값 캐싱)
    • useCallback (함수 캐싱)
  • Memoization: 비용이 많이 드는 함수 호출의 결과를 저장하고 동일한 입력이 다시 발생할 때 캐시된 결과를 반환하여 애플리케이션을 최적화하는 것
    • 컴포넌트 내에 같은 값을 리턴하는 함수를 사용하고 있다면 렌더링할 때마다
      불필요하게 계속 호출할 것
    • 계산된 값을 memoization을 해둔다면 렌더링 할때마다 함수를 새로 할당해서 호출하는 것이 아니라 momoize된 값을 재사용
    • 메모리를 따로 써서 재활용하는 것이기 때문에 꼭 필요할 때만 사용

useMemo

  • 특정 값이 바뀌었을 때만 연산을 하고 이전에 연산했던 결과 재사용
  • 형태
    • useMemo (콜백, deps)
      • 콜백: memoization할 값을 반환하는 함수
      • deps: 배열 안에 있는 값이 바뀔 때만 콜백을 수행해서 momoization된 값을 업데이트
import React, { useState, useMemo } from 'react';

function complicateFunc() {
  //시간이 오래 걸리고 복잡한 연산을 하는 함수
}

function App() {
  const [state, setState] = useState('');
 
  const result = useMemo(() => { 
			return complicateFunc(state);
	}, [state]);

	 ...
}

export default App;

useCallback

  • 함수 자체를 momoization
  • 형태
    • useCallback (콜백, deps)
  • Javscript에서 함수는 객체이기 때문에 컴포넌트가 리렌더링 되면 내부의 함수 변수도 함수 객체를 재할당받게 됨
  • useCallback을 활용하면
    • 컴포넌트가 처음 렌더링 될때만 함수 객체를 만들어서 초기화해주고
    • 리렌더링이 되면 다시 초기화하는 것이 아닌, 이전에 만든 함수 객체를 재활용
profile
잡생각 많은 인간

0개의 댓글