WEB개발/REACT

[REACT] HOOKS (useState, useEffect, useContext, useMemo, useReduce)

wooyeon06 2023. 12. 1. 13:11

 

: 함수형 컴포넌트에서도 상태 관리를 할 수 있는 useState, 그리고 렌더링 직후 작업을 설정하는 useEffect 등의 기능등을 제공하여 기존의 함수형 컴포넌트에서 할 수 없었던 다양한 작업을 할 수 있게 해줍니다.

 

 

1. useState

: useState 는 가장 기본적인 Hook 으로서, 함수형 컴포넌트에서도 가변적인 상태를 지니고 있을 수 있게 해줍니다.

function Counter() {
  const [value, setValue] = React.useState(0);

  console.log(value);

  return (
      <div>
          <p>
              현재 카운터 값은 <b>{value}</b>
          </p>
          
          <button onClick={ ()=> { setValue(value + 1); console.log("plus btn : " + value); } }> +1 button</button>
          <br></br>
          <button onClick={ ()=> { setValue(value - 1); console.log("minus btn : " + value); } }> -1 button</button>

      </div>
  )
}

 

플러스 버튼 누를 시 
plus btn : 0
1

 

다시 Counter가 호출 되면서 +1된 상태

 

 

 

2. useEffect

: useEffect 는 리액트 컴포넌트가 렌더링 될 때마다 특정 작업을 수행하도록 설정 할 수 있는 Hook 입니다. 클래스형 컴포넌트의 componentDidMount 와 componentDidUpdate 를 합친 형태로 보아도 무방합니다.

 

function Counter() {
  const [value, setValue] = React.useState(0);

  console.log(value);

  React.useEffect(() => {
    console.log('렌더링이 완료되었습니다! value : ' + value);
  });

  return (
      <div>
          <p>
              현재 카운터 값은 <b>{value}</b>
          </p>
          
          <button onClick={ ()=> { setValue(value + 1); console.log("plus btn : " + value); } }> +1 button</button>
          <br></br>
          <button onClick={ ()=> { setValue(value - 1); console.log("minus btn : " + value); } }> -1 button</button>

      </div>
  )
}

 

plus btn : 0
1
렌더링이 완료되었습니다! value : 1

 

렌더린이 완료된 후 호출

 

 

만약 useEffect 에서 설정한 함수가 컴포넌트가 화면에 가장 처음 렌더링 될 때만 실행되고 업데이트 할 경우에는 실행 할 필요가 없는 경우엔 함수의 두번째 파라미터로 비어있는 배열

 

React.useEffect(() => {
    console.log('렌더링이 완료되었습니다! value : ' + value);
}, []);

 

 

특정 값이 변경이 될 때만 호출

 

React.useEffect(() => {
    console.log('렌더링이 완료되었습니다! value : ' + value);
}, [value]);

 

 

 

 

3. useContext

이 Hook 을 사용하면 함수형 컴포넌트에서 Context 를 보다 더 쉽게 사용 할 수 있습니다.

context api를 사용하면 전역으로 상태를 관리 할 수 있습니다.

 

 

userInfo.tsx
// 리액트와 함께 createContext를 import 해주기
import React, { ReactNode, createContext, useState } from "react";
import { UserInfo } from "../interface/interfaces";
import axios from "axios";

export const UserContext = createContext({});

// UserStore라는 이름으로 어떤 데이터를 만들지 생성
const UserStore = (props : {children : ReactNode}) => {

    const [user, setUser] = useState<UserInfo>({
        isLogin : false,
        id: "",
        name: "",
        pw: "",
    });

    return (
        // Provider를 사용해서 감싸주기
        <UserContext.Provider value={{user, setUser}}>
            {props.children}
        </UserContext.Provider>
    );
};

export default UserStore;

 

 

page1.tsx
const context : any = useContext(UserContext);
const user = context.user;

return(
    <div>
        <p>This is Page1 .... login userID : <b>{user.id}</b></p>
    </div>
);

 

 

 

4. useMemo

성능 최적화를 위해 사용됩니다. 이 훅은 메모이제이션된 값을 반환하여 불필요한 계산을 피하고 컴포넌트의 성능을 향상시키는 데 도움을 줍니다.

const <콜백 함수 결과 값>] = useMemo(<콜백 함수>, <두 번째 인자로 의존성 배열>)

 

 

import React, { useMemo } from 'react';

const MyComponent = ({ data }) => {
  // useMemo를 사용하여 계산 결과를 메모이제이션
  const expensiveCalculation = useMemo(() => {
    // 여기서 data에 기반한 계산을 수행
    // 예: 복잡한 계산 또는 데이터 필터링
    return data.filter(item => item.someCondition).map(item => item.someValue);
  }, [data]); // data가 변경될 때만 다시 계산

  return (
    <div>
      {/* expensiveCalculation 값을 사용하는 UI */}
      {expensiveCalculation.map((value, index) => (
        <span key={index}>{value}</span>
      ))}
    </div>
  );
};

export default MyComponent;

 

 

 

5. useReduce

const [<상태 객체>, <dispatch 함수>] = useReducer(<reducer 함수>, <초기 상태>, <초기 함수>)

 

 

redux가 뭔지 알았다면 이제 redux의 구조에 대해 알아보겠습니다.

redux에서 중요한 것은 상태를 직접 변경하는 것이 아닌 "action"을 통해 변경하는 것입니다. 즉, redux에서 중요한 것은 다음과 같습니다.

state
action
reducer

 

추가로 dispatch, selector는 예제를 진행하며 설명하겠습니다.

  • state: 위의 내용과 동일, store에서 저장되어 있는 값.
  • action: store에 저장된 값을 변경시키는 방식을 정함.
  • reducer: (action, old state)를 받아서 new state로 변환시키는 함수.

 

const reducer = (state : any, action : string) => {
    switch (action) {
        case 'increment':
            return state + 1
        case 'decrement':
            return state - 1
    }
}

function Page3() {
    const [count, dispatch] = React.useReducer(reducer, 0)

    return (
        <div>
            Count: {count}
            <button onClick={() => dispatch('increment')}>Increment</button>
            <button onClick={() => dispatch('decrement')}>Decrement</button>
        </div>
    )
}

 

 

 

6. useImperativeHandle

:  부모 컴포넌트에서 자식 컴포넌트의 State를 관리해야할 경우 사용할 수 있는 훅입니다.

const iframeRef = useRef<any>(null);
    //부모에서 호출...
    useImperativeHandle(ref, () => {
        return {
            //iframe에 데이터 전달
            postMesseageChild: () => {
                if (iframeRef && iframeRef.current) {
                    console.log("[iframe < parents] postMesseageChild", labels);

                    const newData: any = {};
                    labels.map((item) => {
                        newData[item.labelId] = item.labelText1;
                    });

                    iframeRef.current.contentWindow.postMessage(
                        {
                            type: "label",
                            changeLabelList: newData
                        },
                        preview_domain,
                    );
                }
            },
        };
    }, [labels]);

 

<Button
imageUrl="/images/dot-right-arrow.png"
className="basic-btn mt-2 flex h-7 items-center justify-start"
onClick={() => {
		if (previewRef && previewRef.current) {
			previewRef.current.postMesseageChild();
		}
	}}>PREVIEW 적용
</Button>

 


https://velog.io/@velopert/react-hooks

https://velog.io/@jinyoung985/React-useMemo%EB%9E%80

https://velog.io/@qf9ar8nv/%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%98%88%EC%A0%9C%EB%A5%BC-%ED%86%B5%ED%95%B4-Redux%EB%A5%BC-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0