React Hooks란?

2023. 3. 5. 16:17프레임워크(Framework)/React

[React Hooks란?]

: React에서 기존에 사용하던 Class를 이용한 코드를 작성할 필요 없이, state와 여러 React 기능을 사용할 수 있도록 만든 라이브러리

 

[React Hook 개발 과정]

1. class component 사용

2. 함수 컴포넌트와 리액트 훅 사용 (리액트 훅은 함수 컴포넌트에서만 사용 가능하다)

함수 컴포넌트가 어떤 값을 유지할 수 있도록, '캐시'를 만들었다.
이 캐시를 이용하고자 만든 여러 개의 API를 '리액트 훅' 함수라고 부른다.
※API란?
=> 여러 프로그램들과 데이터베이스 그리고 기능들의 상호 통신 방법을 규정하고 도와주는 매개체이다.

 

[React Hook의 필요성]

함수 컴포넌트도 클래스 컴포넌트처럼 사용할 수 있다.

함수 컴포넌트는 클래스 컴포넌트와 다르게, 모듈로 활용하기가 쉬우므로 서로의 장점을 전부 다 가지고 있다.

 

[React Hooks 사용규칙]

같은 Hook을 여러 번 호출할 수 있다

export default function App() {
    const [value1, setValue1] = useState()
    const [value2, setValue2] = useState()
    return {
        <div>
            <div>{value1}</div>
            <div>{value2}</div>
        </div>
    }
}

 
 
함수 컴포넌트 몸통이 아닌, 몸통 안 복합 실행문의 { }에서는 사용할 수 없다

(js의 block scope는, block 외에서 사용할 수 없으므로 (지역변수이기 때문에))

export default function App() {
    return {
        <div>
            //불가능
            <div> {const [value, setValue] = useState()}</div>
        </div>
    }
}

 
 
비동기 함수(async 키워드가 붙은 함수)는 콜백함수로 사용할 수 없다.

export default function App() {
    //useEffect Hook 내부에, 비동기 함수가 들어가므로 에러 발생
    useEffect(async () => {
        await Promise.resolve(1)
    }, [])
    
    return {
       <div>
           <div>Test</div>
       </div>
   }
}

※비동기 함수란?
=> 호출부에서 실행 결과를 기대리지 않아도 되는 함수
     ( = 특정 코드의 처리가 끝나기 전에 다음 코드를 실행할 수 있는 것을 뜻함)
 
 

 


 
 

[React에서 기본적으로 지원하는 Hooks]

1. useState

컴포넌트의 state(상태)를 관리할 수 있다.
상태에 따라, 다른 화면 출력

2. useEffect

렌더링 이후에 실행할 코드를 만들 수 있다.
어떤 변수가 변경될 때마다(의존성), 특정 기능이 작동하도록 할 수 있다.

3.useContext

부모컴포넌트와 자식컴포넌트 간의 변수와 함수를 전역적으로 정의할 수 있다.

4. useReducer

state(상태) 업데이트 로직을, reducer 함수에 따로 분리할 수 있다.

5. useRef

컴포넌트나 HTML 요소를 레퍼런스로 관리할 수 있다.

6. forwardRef

useRef로 만든 레퍼런스를 상위 컴포넌트로 전달할 수 있다.

7. useImperativeHandle

useRef로 만든 레퍼런스의 상태에 따라, 실행할 함수를 정의할 수 있다.

8. useMemo, useCallback

의존성 배열에 적힌 값이 변할 때마 값, 함수를 다시 정의할 수 있다.(재랜더링시 정의 안함)

9. useLayoutEffect

모든 DOM 변경 후 브라우저가 화면을 그리기(render) 전에 실행되는 기능을 정할 수 있다.

10. useDebugValue

사용자 정의 Hook의 디버깅을 도와준다.

 
 
 


[useState 사용법]

state란?

React에서 사용자의 반응에 따라, 화면을 바꿔주기(렌더링) 위에 사용되는 트리커 역할을 하는 변수

React가 state를 감시하고, 바뀐 정보에 따른 화면을 표시해준다.

(state와 setState 함수의 반환값을 비교)

state만들기( useState 사용)

//React에 기본적으로 내장되어 있는 useState 훅을 사용하면, state를 만들 수 있다.
import {useState} from "react";
//const [state, state변경함수] = useState(기본 state값);
const [isLoggedIn, setIsLoggedIn] = useState(false);

state 변경하기(사용자 반응에 따른 다른 화면 보여주기)

//전에 만든 "isLoggedIn" state의 값을 true로 변경한다.
setIsLoggedIn(true);
//**useState함수를 사용해 만든 "state 변경 함수"를 사용하여야 한다.

state의 특징

state는 어디에서든지 사용가능하다.
하지만 컴포넌트는 시간이 지나고, 앱이 커질수록 점점 많아지므로
점점 관리가 어려워지는 단점이 있다.

 


useEffect와 useLayoutEffect의 차이점

useEffect useLayoutEffect
비동기 방식 동기 방식(끝날 때까지 React가 기다려줌)

useEffect 사용법

//React에 기본적으로 내장되어 있는 useState와 useEffect 불러오기
import {setState, useEffect} from "react";
...
function App() {
    const [data, changeData] = setState(false)
    //useEffect(실행할 함수, 트리거가 될 변수)
    useEffect(() => {
        if(data.me === null) {
            console.log("Data changed!")
        }
        
        return () => console.log("컴포넌트 파괴, 언마운트됨")
    }, [data]);
    
    //data 변수가 바뀔 떄마다, react가 이를 감지해, 콘솔창에 "Data changed!" 출력
    
    return (
        <div>
            <button valueu="적용" onClick={changeData(!data)} />
        </div>
    )
}

export default App;

 useLayoutEffect 사용법

//React에 기본적으로 내장되어 있는 useState와, useEffect 불러오기
import {setState, useLayoutEffect} from “react”;
…
function App() {
    const [data, changeData] = setState(false)
    
    //useLayoutEffect(실행할 함수, 트리거가 될 변수)
    useLayoutEffect(() => {
        if(data.me ===null) {
            console.log(“Data changed!”)
        }
        
        return () => console.log(“Layout 사라짐“)
    }, [data]);
    
    //data변수가 바뀔 때마다, react가 이를 감지해, 콘솔창에 ”Data changed!” 출력
    return (
        <div>
            <button value=“적용“ onClick={changedData(!data)} />
        </div>
    )
}
export default App;
//주의:export default로 하면 함수 이름이 변경될 수 있으므로 위에서 export function App으로 바꿔주는 것이 좋음

useContext 사용법

새로운 context 만들기

//nextContext.js
import {createContext} from “react” //createContext 함수 불러오기
//context안에 homeText란 변수를 만들고, 공백(“”) 문자를 저장한다.
const newContext = createContext({
    homeText: “”,
})

context를 사용할 부분 선택하기, 새로운 정보 저장하기

//App.js
import React from “react”;
import {View} from “react-native”;
import Home from “./Home”; //자식 컴포넌트 불러오기

import {newContext} from “./newContext”; //context불러오기

export default function App() {
    //context에 저장할 정보를 입력한다.
    const homeText = “isWorked!”
    
    //NewContext.Provider로 우리가 만든 context를 사용할 부분을 감싸준다.
    return (
        <newContext.Provider value={{homeText}}>
            <View>
                <Home />
            </View>
        </newContext.Provider>
    );
}

context 사용하기

//Home.js
import React from “react”;
import {Text, View} from “react-native”;

import {useContext} from “react”;
import {newCOntext} from “../newContext”;

export default function Home() {
    //useContext hook 사용해서, newContext에 저장된 정보 가져오기
    const {homeText} = useContext(newContext);
    
    //불러온 정보 사용하기!
    return(
        <View>
            <Text>{homeText}<Text>
        </View>
    );
}

context 사용시 좋은 점

React에서 context 없이 변수나 함수를 다른 컴포넌트로 전달하려면
부모자식간에만 전달이 가능하므로
컴포넌트가 많아질수록 불필요한 컴포넌트를 많이 거쳐야 하는 문제가 발생한다
(A->B->C->D)
context를 이용하면, 중간과정을 재치고 직통으로 전달할 수 있다(A->D)

context 사용시 유의점

전달하고자 하는 컴포넌트에 context를 만들면, 불필요한 호출이 추가될 수 있으므로, context 전용 파일을 만들어야 한다.
예시 조건)
컴포넌트 : A, B, C, D (A가 최상위 컴포넌트)
전달 : A->D
A 컴포넌트에서 context 생성
D 컴포넌트에서 context 불러옴
실행 과정)
1. A가 렌더링되며, B, C, D를 차례로 불러옴
2. D에서 context를 가져오기 위해 A를 다시 불러옴
3. A를 다시 불러오면서, 불필요한 중복이 발생함

useMemo 사용법

Memorization : 과거에 계산한 값을 반복해서 사용할 때, 그 값을 캐시에 저장하는 것
-재귀함수에 사용하면, 불필요한 반복 수행을 대폭 줄일 수 있다.
-의존성을 가지므로, 첫렌더링시에만 저장할 변수를 설정할 수 있다.
export default function App() {
    const data = useMemo(() => “data”, []);
    //데이터 변수는 의존성 배열 []에 따라 선언된다. ([]사용시, 첫 렌더링 시에 1번만 선언)
    return <></>
}

 


useCallback 사용법

useMemo의 함수버전, 반복적으로 사용되는 함수를 캐시에 저장할 수 있다

 

export default APP() {
    //괄호() 속에 초기값 입력
    const viewRef = useRef(null)
    
    //viewRef 객체에 있는 메소드를 사용한다.
    function testFunc() {
        viewRef.current.메소드()
    }
    
    //viewRef요소에, 해당 메모리 주소 전달
    return <View ref={viewRef}>
        <Text>Tes</Text>
    </View>
}

useRef 사용법

HTML요소(태그)나 컴포넌트의 메모리주소를 가져와, 객체(레퍼런스) 형식으로 관리할 수 있다.
-current 속성을 가지고 있는 객체를 반환한다.
-current 값이 바뀌어도, 재렌더링 되지 않는다.
-재렌더링시에도, current값은 없어지지 않는다.
export default function App() {
    const avatarPressed = useCallback(() => Alert.alert(‘avatar pressed.’), []);
    return <></>
}

forwardRef 사용법

부모 컴포넌트와 자식 컴포넌트가 [함수형 컴포넌트]로 정의되었을 때,
부모 컴포넌트에서 자식 컴포넌트를 객체 형식으로 관리할 수 있다.
//자식 컴포넌트
import {forwardRef} from “react”

//부모 컴포넌트의 ref를 인자로 받아올 수 있다.
const _TextInput = (
    props,
    ref
) => {
    //자식 컴포넌트의 ref에 useRef()객체 전달
    return(
        <View>
            <Text>Hello</Text>
            <TextInput
                ref={ref}
                {…props}
            />
        </View>
    );
};
//forwardRef()으로 감싸주기
export const MyTextInput = forwardRef(_TextInput);
//부모 컴포넌트
import {useRef} from ‘react’

const Parent = () => {
    const testRef = useRef(null);
    const setFocus = () => testRef.current?.focus();
    
    //자식 컴포넌트에게 ref 전달
    return <ImperativeTextInput ref={testRef} />
};

useImperativehandle 사용법

useRef으로 만든 컴포넌트 객체(레퍼런스) 안에 커스텀 메서드(함수)를 만들 수 있다.
import {useRef, useImperativeHandle} from ‘react’

const ImperativeTextInput = (props, ref) => {
    const textInputRef = useRef<TextInput | null>(null);
    
    //ref을 전달받아, 메소드를 만들어준다.(다른 ref호출 가능)
    useImperativeHandle(
        ref,
        () => ({
            focus() {
                textInputRef.current?.focus();
            },
            dismiss() {
                Keyboad.dismiss();
            },
        }),
          [],
    );
    return <TextInput ref={textInputRef} {…props} />;
};

export default forwardRef(ImperativeTextInput);
dfd

useReducer 사용법(Redux 개념)

여러 개의 상태를 통합해 관리한다.
state : 상태
action : 변화내용 객체
reducer : state와 action 인자로 받아, 다음 상태 반환하는 함수
dispatch : action을 반환하는 함수


필요성

useState를 여러 번 사용하는 경우 유용하다
상태를 업데이트하는 로직을, 컴포넌트 밖에 작성 가능
dispatch라는 함수, 상태 통합 관리 -> Context와 함께 사용하면 유용?

 

//동시에 여러 상태값 업데이트
const onPress = () => {
    setMode('hype');
    setText('We are Hyped!');
    setRed(true);
}

//dispatch에게 인자를 전달받음
function reducer(state, action) {
    //state에 이전 상태값 들어있음
    //action에 {키:값, ...} 형식의 객체(action)가 들어있음
    switch (action.type) {
        case 'hype':
            //상태값 변환
            return {text: acxtion.text, red: true};
        case 'carm':
            return {...state, red: false};
        default
            throw new Error('action type이 없습니다.');
    }
}
    
function Sence() {
    //1번쨰 : reducer 함수, 2번쨰 : 초기 상태값
    const [state, dispatch] = useReducer(reducer, initialState);
        
    //dispatch 함수에 {키:값, ...} 형식의 객체를 전달
    const onChange = () => dispatch({type: 'hype', text: 'We are Hyped!'});
    const onDecrese = () => dispatch({type: 'carm'});
    ...
}

 


cache

React.js는 컴포넌트 내부에서 전역변수를 사용할 수 있도록,
cache 객체 안에, key:value 형식으로 저장하는 방식을 사용한다.
//전역적인 캐시 생성
const cache: Record<string, any> = {};

export default function createOrUse<T>{key: string, callback: () => T) {
    if(!cache[key]) {
        cache[key] = callback();
        //키 : 값 저장
    }
    return cache[key];
    //입력한 키에 해당하는 값 반환
}

의존성

React.js의 캐시는, 상황에 따라 업데이트하도록 만드는 의존성 배열을 만들 수 있다.
(배열 속의 값이 바뀔 떄마다, 캐시를 업데이트 한다.)
useEffect(() => {
    console.log("dependency")
}, [의존성1, 의존성2 ...])

/*
    1. 의존성 변경
    2. cache 내부에서, 의존성에 해당하는 key 찾아서 업데이트
    3. React가 컴포넌트를 재랜더링함
*/

 

'프레임워크(Framework) > React' 카테고리의 다른 글

React 기본 파일(index.js, App.js, index.html)  (0) 2023.03.10
[React Query] 리액트 쿼리 시작하기 (useQuery)  (0) 2023.03.06
Hooks  (0) 2023.02.18
State and Lifecycle  (0) 2023.02.18
Components and Props  (1) 2023.02.17