react - hooks

김예찬·2021년 4월 22일
0

react hooks에 대해 정리합니다😊

Hook

React 버전 16.8부터 React요소로 새로 추가된 개념으로써, functionall component에서도 class component의 라이프사이클 기능을 사용할 수 있게 해주는 것입니다.

리액트 팀에서는 클래스 컴포넌트의 상태 관리를 위해서는 this라는 자바스크립트의 개념을 알아야 하는데, 자바스크립트의에서의 this는 다른 언어의 this와는 다른 성격을 가지고 있어 사용하기 까다로운 점이 있다고 보고, 이 사항이 react의 사용에 좀 더 어려움을 가져다 준다고 보았습니다. 따라서 hooks라는 기능들을 추가함으로써 function 컴포넌트의 사용을 권장하고 있습니다.

🍕 클래스 컴포넌트를 제거할 계획은 없음으로 클래스 컴포넌트에 익숙하다고 해도 걱정 NO!


useState

  • 가장 기본적인 Hook.
  • 함수형 컴포넌트에서도 가변적인 상태를 지닐 수 있게 해줌(클래스의 this.state)
import React, { useState } from 'react';

function Example() {
 const [count, setCount] = useState(0);
 return (
   <div>
     <p>You clicked {count} times</p>
     <button onClick={() => setCount(count + 1)}>
       Click me
     </button>
   </div>
 );
}

useEffect

  • 컴포넌트가 렌더링될 때마다 특정 작업을 수행하도록 설정할 수 있는 Hook
  • 클래스형의 componentDidMount와 componentDidUpdate를 합친 형태라고 볼수 있음
import React, { useState, useEffect } from 'react';

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

  // componentDidMount, componentDidUpdate와 비슷합니다
  useEffect(() => {
    // 브라우저 API를 이용해 문서의 타이틀을 업데이트합니다
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
  • 마운트 될 때만 실행하고 싶다면(랜더링되는 처음 순간), 2번째 파라미터로 비어있는 배열을 넣어주면됨
	useEffect(() => {
      console.log('마운트 될 때만 실행');
   	}, []);
  • 특정 값이 업데이트될 때 실행하고 싶다면 배열에 그 값을 명시해주면 됨.

  • 컴포넌트가 언마운트되기 전이나 업데이트되기 직전에 어떠한 작업을 수행하고 싶다면 뒷정리 함수를 반환해 주어야 함

	useEffect(() => {
      console.log('마운트 될 때만 실행');
      
      return () => {
        console.log('언마운트 전이나 업데이트 되기 직전 실행')
      }
   	}, []);

useReducer

  • 리듀서를 사용하는 hooks
  • 리듀서를 사용하면 상태관리 로직을 컴포넌트에서 분리해서 사용할 수 있음.
  • action 객체를 dispatch함으로써 상태 값 변경
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: 'INCREMENT})}+1</button>
    </div>
  )
}

useMemo

  • 함수형 컴포넌트 내부에서 발생하는 연산을 최적화할 수 있는 hook
  • 값을 메모이제이션해, 변화가 있을 때만 다시 실행.
import React, { useMemo } from "react";

const colorKor = useMemo(() => getColorKor(color), [color]);
const movieGenreKor = useMemo(() => getMovieGenreKor(movie), [movie]);
  • 위에 예시에서 2번째 파라미터로 들어온 값들(state, props 등 가변적 변수들이 될 수 있음)이 변화가 있다면 1번째 함수 파라미터의 내용이 실행되며 새로운 값을 반환, 그렇지 않다면 이전에 캐싱해둔 값을 사용

useCallback

  • useMomo가 값을 캐싱해 둔다면, useCallback은 함수 자체를 캐싱해둠.
  • react는 가상 돔을 랜더링할 때, 변화를 감지하고 변화가 있는 부분을 다시 그려내는데 이때 만약 inline 스타일로 객체가 들어 있다면, 이는 무조건 다시 그려내게 됨

🍕 객체는 키와 값 상관 없이 {} === {}는 false입니다. 같은 객체를 변수로 담고 있는 경우가 아니라면 모든 객체는 다른 주소값을 가지고 있다고 생각하면 됩니다!!!.


// 이번 예제는 벨로퍼트님의 react 강의에서 참고 했습니다.
import React, { useRef, useState, useMemo, useCallback } from 'react';
import UserList from './UserList';
import CreateUser from './CreateUser';

function countActiveUsers(users) {
  console.log('활성 사용자 수를 세는중...');
  return users.filter(user => user.active).length;
}

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: ''
  });
  const { username, email } = inputs;
  const onChange = useCallback(
    e => {
      const { name, value } = e.target;
      setInputs({
        ...inputs,
        [name]: value
      });
    },
    [inputs]
  );
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'velopert',
      email: 'public.velopert@gmail.com',
      active: true
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com',
      active: false
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com',
      active: false
    }
  ]);

  const nextId = useRef(4);
  const onCreate = useCallback(() => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  }, [users, username, email]);

  const onRemove = useCallback(
    id => {
      // user.id 가 파라미터로 일치하지 않는 원소만 추출해서 새로운 배열을 만듬
      // = user.id 가 id 인 것을 제거함
      setUsers(users.filter(user => user.id !== id));
    },
    [users]
  );
  const onToggle = useCallback(
    id => {
      setUsers(
        users.map(user =>
          user.id === id ? { ...user, active: !user.active } : user
        )
      );
    },
    [users]
  );
  const count = useMemo(() => countActiveUsers(users), [users]);
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
      <div>활성사용자 수 : {count}</div>
    </>
  );
}

export default App;
  • 주의할 점은 callback되는 함수 안에서 사용되어지는 변화 가능성 있는 값이 있다면, 2번째 파라미터로 꼭 넣어주어야 함

useRef

  • react 함수형 컴포넌트에서 DOM 객체를 직접 접근해 사용할 수 있게 해주는 hook
  • useRef를 통해 만든 객체 안의 current라는 값에 DOM 객체가 담기게 됨
import React, {useRef, useState} from 'react'
  
const Example = () => {
  
 const inputEl = useRef();
 const [number, setNumber] = useState(0);
  
 const onChange = (e) => {
  setNumber(e.target.value);
  inputEl.current.focus(); // onChange 함수가 호출 될 때마다 input dom에 focus가 됨
 }
  
 return (
 	<input ref={inputEl} value={number} onChange={onChange}/>
 )
}

커스텀 Hook

이 이외에도 많은 커스텀 hook이 존재합니다. 다른 개발자가 만든 다양한 Hooks를 사용하고 싶다면 다음 링크에서 확인할 수 있습니다.

profile
프론트엔드

0개의 댓글