카테고리 없음

리액트 Hooks

ㅈ현 2022. 7. 2. 19:14

Hooks는 함수형 컴포넌트에서도 클래스형 컴포넌트의 State사용이라던지, 라이프 사이클 메소드와 같은 기능들을 사용할 수 있게 해준다. 

https://reactjs.org/docs/hooks-overview.html

 

Hooks at a Glance – React

A JavaScript library for building user interfaces

reactjs.org

 

useState

useState는 컴포넌트의 가변상태를 나타내줄 수 있는 hook이다. 이 hook을 통해 컴포넌트 내 state를 유지하며, state가 setter를 통해 업데이트 되게되면 뷰를 갱신하게 됩니다.

 

Counter.js

import React, {useState} from "react";

function Counter() {    
    const [n, set_n] = useState(0);
    return (
        <>            
            <div>{n}</div>
            <button
                onClick ={() => {
                    set_n(n => n += 1)
                }}
            >up</button>
        </>
    )
}
export default Counter;

useState는 다음과 같이 [state변수, setter] 의 형태로 사용하게 된다. 

위 예제는 n이라는 state를 만들고 set_n이라는 setter를 통해 n 의 값을 변경시키는 예제다.

이처럼 useState는 state를 관리할 수 있게 해준다.

state업데이트 전
state업데이트 후

 

useState의 setter의 파라미터로는 두가지 형태의 값을 전달해줄 수 있다.

 

useState의 비동기 처리

useState는 비동기 처리를 통해 state값을 업데이트 하게 된다. setter 사용시 바로 업데이트를 진행하지 않고, 일정 시간동안(16ms) 변화된 state를 모아 한번에 처리하는 배치 처리 방식을 사용한다. 이런 방식을 사용함으로 인해 부하를 최대한 줄일 수 있게된다.

 

다음 예제는 a의 값을 두번 더하는 예제다

Counter.js

import React, {useState} from "react";

function Counter() {    
    const [a, set_a] = useState(0);
    return (
        <>            
            <div>{a}</div>
            <button
                onClick ={() => {                
                    set_a(a + 1)
                    set_a(a + 1)
                }}
            >up</button>
        </>
    )
}
export default Counter;

다음 예제에서는 클릭 이벤트가 일어나게 되면 a값에 1을 더하는 setter가 두번 실행되게 된다. 하지만 실제 동작을 보게되면

 

a 값이 +1 만 된것을 알 수 있다.

이는 a 가 0일때, 클릭이 일어나게 되면, 첫번째 setter를 통해 a의 값이 1로 세팅되게 된다. 하지만 배치 시간 이내에 두번째 setter가 실행되게 되면, a의 값이 아직 0일때 setter가 a + 1로 세팅되게 된다. 즉 첫번째, 두번째 setter 모두 a의 값을 1로  set 하게 되고, a의 값은 +1 밖에 되지 않는다.

 

해결책

setter의 파라미터로 함수를 넘기면 된다. 해당 함수의 파라미터는 기존 state값을 받게 된다.

 

Counter.js

import React, {useState} from "react";

function Counter() {    
    const [a, set_a] = useState(0);    
    return (
        <>            
            <div>{a}</div>
            <button
                onClick ={() => {                
                    set_a(prev_a => prev_a + 1)
                    set_a(prev_a => prev_a + 1)
                }}
            >up</button>
        </>
    )
}
export default Counter;

위 예제처럼 업데이트 함수를 파라미터로 넘기게 되면, 배치 처리를 할때 두번의 함수가 실행되게 되므로 a의 값이 +2가 되게 된다.

 

useEffect

useEffect는 컴포넌트가 랜더링 될때마다 실행되게 된다.

 

useEffect에는 3가지 구성요소가 있다.

1. context

2. cleanup function

3. dependency

 

context는 useEffect의 행동을 정의하는 구문이다.

cleanup은 컴포넌트가 unmount 되거나, update될때 실행되는 함수다 (return을 통해 전달된다)

dependecny는 컴포넌트에서 해당 state값이 변경되었을때만 useEffect가 실행되도록 하는 의존관계 설정이다.

 

import React, {useEffect, useState} from "react";

function Counter() {    
    const [a, set_a] = useState(0);    
    useEffect(() => {
        console.log(a)
    }, [])
    return (
        <>            
            <div>{a}</div>
            <button
                onClick ={() => {                
                    set_a(prev_a => prev_a + 1)                    
                }}
            >up</button>
        </>
    )
}
export default Counter;

useEffect는 컴포넌트가 빌드된 이후 실행되게 된다. 해당 예시에선 컴포넌트가 빌드된 이후 a의 값인  0을 로그로 출력하게 된다.

하지만 dependency가 없기 때문에 버튼에 의해 컴포넌트가 업데이트 될때는 로그가 출력되지 않는다.

만약 state a가 변경될때마다 useEffect가 실행되게 하기 위해선 useEffect의 dependency 배열에 state를 추가하여 준다.

useEffect(() => {
    console.log(a)        
}, [a])

 

해당 컴포넌트가 언마운트시 실행될 cleanup 함수를 넣어 해당 컴포넌트를 언마운트 시켜본다.

 

Counter.js

import React, {useEffect, useState, useRef} from "react";

function Counter() {    
    const [a, set_a] = useState(0);            
    useEffect(() => {
        console.log(a)    
        return () => {
            console.log("clean up!")
        }    
    }, [a])
    return (
        <>            
            <div>{a}</div>
            <button
                onClick ={() => {                
                    set_a(prev_a => prev_a + 1)                    
                }}
            >up</button>            
        </>
    )
}
export default Counter;

useEffect의 리턴으로 함수 하나를 리턴시켰다.

 

App.js

import react, {useState} from "react"
import Counter from './Counter';


function App() {
  const [visible, set_visible] = useState(true)
  return (
    <div>
      {visible && <Counter/>  }     
      <button
        onClick = {() => {
          set_visible(false)
        }}
      >clean
        </button>     
    </div>
  );
}

export default App;

해당 Counter 컴포넌트를 사용하는 App.js에서는 해당 컴포넌트를 언마운트 시킬 버튼을 만들어 해당 컴포넌트를 언마운트 시켜본다.

초창기 App 컴포넌트, Counter 컴포넌트와 버튼이 같이 있다.

 

이제 버튼을 눌러 Counter 컴포넌트를 없애본다.

Counter 컴포넌트 언마운트

이제 콘솔창에는 cleanup 함수로 등록된 로그가 출력되게 된다.

 

cleanup 함수가 동작하며 로그 출력

 

useRef

리액트에서 실제 DOM에 접근해야 할때가 있다. 이럴때 React 에서는 ref를 사용한다. 함수형 컴포넌트에서 이러한 ref에 접근할 수 있게 하는것이 useRef이다.

이러한 ref는 실제 DOM을 직접 다뤄야 할때 (focus작업 또는 canvas, animation 작업) 사용된다.

 

사용법은 useRef를 통해 레퍼런스 변수를 만든후

const ref_dom = useRef(null);

JSX에서 ref={}의 형태로 ref를 연결시켜준다.

<div ref={ref_dom}></div>

 

전체적인 예제는 다음과 같다.

Counter.js

import React, {useRef} from "react";

function Counter() {                
    const ref_dom = useRef(null);    
    return (
        <>            
            <div ref={ref_dom}>ref dom</div>
            <button
                onClick ={() => {                                    
                    console.log(ref_dom)
                }}
            >up</button>            
        </>
    )
}
export default Counter;

div 박스 하나를 ref 변수로 연결시킨 후, 버튼을 누르면 해당 변수를 출력하는 예제다.

버튼을 누르게 되면 해당 변수의 .current 필드에 div 요소가 들어가있는것을 알 수 있다.

 

 

이러한 ref는 focus를 해야할때 사용될 수 있다.

다음 예제는 버튼을 누르면 텍스트 input으로 포커스가 바뀌게 되는 예제다.

 

Counter.js

import React, {useRef} from "react";

function Counter() {                
    const ref_dom = useRef(null);    
    return (
        <>            
            <input ref={ref_dom}/>
            <button
                onClick ={() => {                                    
                    ref_dom.current.focus()
                }}
            >up</button>            
        </>
    )
}
export default Counter;

버튼을 누르게 되면 다음과 같이 focus가 텍스트 입력창으로 옮겨가게 된다.