본문 바로가기
React

[React] React hooks(useMemo,useContext,useReducer)의 개념 및 사용방법

by 개발적금 2023. 3. 13.

이번 포스팅에서는 React hooks의 세가지를 알아보겠습니다.


 

1. useMemo

 

2. useContext

 

3. useReducer

 


 

 

1. useMemo

 

`useMemo`는 React hooks중 하나로, 계산비용이 많이 드는 함수의 결과값을

기억하고, 재사용할수 있도록 해줍니다. 이를 통해 성능 최적화를 할 수 있습니다.

 

즉, 계산비용이 많이 드는 함수를 `메모이제이션`하여 이전에 계산된 결과를 캐시하고,

같은 인자로 호출될때 이전에 계산된 결과를 재사용합니다.

 

function Component(){
 const value = calculate()
 return <div>{value}</div>
}

function calculate(){
 return 10
}

 

위의 코드를 보면 , Component가 랜더링 될때마다 value라는 변수는

초기화 됩니다. 따라서 calculate() 가 반복적으로 호출됩니다. 만약

calculate()가 무거운 일을 하는 함수라면 굉장히 비효율적입니다.

useMemo를 사용하여 바꾸어 보자면

 

const value = useMemo(() => {
  return calculate()
},[item])

 

useMemo는 첫번째 인자로 콜백함수를 받고, 두번째 인자로 의존성 배열을 받습니다.

두번째 인자인 배열요소 값이 업데이트될때만 콜백함수를 다시 호출해서 

`메모이제이션` 된 값을 업데이트 해줘서 다시 메모이제이션을 해줍니다.

만약 빈배열( [] )을 넘겨주면 맨처음 컴포넌트가 마운트 되었을때만 값을 계산하고

이후에는 계속 메모이제이션 된 값을 꺼내와서 사용합니다.

 

※ 꼭 필요할때만 사용

값을 재활용하기 위해 따로 메모리를 소비하여 저장을 해놓는 것입니다. 

그렇기때문에 불필요한것들을 메모이제이션 한다면 성능이 안좋아질수 있습니다.

 

 

2. useContext

 

useContext는 상위 컴포넌트에서 하위 컴포넌트로 데이터 전달하는것을 

간편하게 해줍니다. useContext를 사용하면 중간에 위치한

컴포넌트가 데이터를 전달하는것이 필요 없어지므로,

컴포넌트 간의 연결이 단순해지고, 코드도 간결해집니다.

 

보통 부모 컴포넌트에서 자식 컴포넌트로 props를 통해 데이터를 전달하는데

100개의 컴포넌트가 있다면...? 부모에서 자식으로 순차적으로 전달하는데

엄청난 노가다가 필요할것 입니다

Context를 사용하여 전역적으로 데이터관리를 할수있습니다.

 

`useContext`는 두개의 인자를 받습니다.

첫번째는 React.createContext()로 생성한 Context 객체이며,

두번째는 기본값 입니다.

 

import {craeteContext,useContext} from 'react'

 

사용하기전에 createContext와 useContext를 import 받습니다.

 

--- AgeContext.js ---

import {createContext} from 'react'

export const AgeContext = createContext(null)

----------------------


--- NameContext.js ---

import {createContext} from 'react'

export const NameContext = createContext(null)

----------------------


--- App.js ---

import {AgeContext} from './Context/AgeContext'
import {NameContext} from './Context/NameContext'
import Header from 'Header'

function App() {
return (
	<AgeContext.Provider value="33">
      <NameContext.Provider value="개발적끔">
        <div className="App">
          <Header/>
        </div>
      </NameContext.Provider>
    </AgeContext.Provider>
 )
}

--------------

 

 

-> React hooks의 useContext를 사용하기 위해 AgeContext / NameContext에

초기값 Null을 설정하였습니다. 

-> App.js에서 AgeContext.Provider 와 NameContext.Provider 를 사용하여 

트리 안에 포함된 컴포넌트에 값을 전달하였습니다. 

 

--- Header.js ---

import {useContext} from 'react'
import {AgeContext} from './Context/AgeContext'
import {NameContext} from './Context/NameContext'

const Header = () => {
	const age = useContext(AgeContext)
    const user = useContext(NameContext)
    
    console.log('age :: ', age)
    console.log('name :: ', name)
    
    return(
    	<header>
        	<p> 안녕하세요 {name}님 환영합니다.
            	{name}님의 나이는 {age}입니다.
            </p>
        </header>
    )

}


export default Header



-----------------

 

 

3. useReducer

 

`useReducer`는 [ 상태와 / 상태를 변경하는 함수 ] 를 하나의 객체로 묶어서 관리하는

기능을 제공합니다. useReducer는 컴포넌트의 상태 업데이트 로직을 컴포넌트 바깥으로

분리하여 재사용성을 높이고, 복잡한 상태 업데이트 로직을 구현할때 편리합니다..

 

 

 

const [state,dispatch] = useReducer(reducer,initialState)

----------------------------------------------------

1. state

 - 컴포넌트에서 사용할 상태

2. dispatch 함수

 - 첫번째 인자인 reducer 함수를 실행시킨다.

 - 컴포넌트 내에서 state의 업데이트를 일으키키 위해 사용하는 함수

3. reducer 함수

 - 컴포넌트 외부에서 state를 업데이트 하는 함수

 - 현재 state, action 객체를 인자로 받아, 기존의 state를 대체하여 새로운 state를 반환하는 함수

4. initialState

 - 초기 state

 

`useReducer`는 두개의 인자를 받습니다. 

첫번째 인자는 reducer 함수이며 두번째 인자는 초기 상태값입니다.

 

`리듀서 함수는 이전 상태값과 액션 객체를 받아서 새로운 상태값을 반환하는

함수`입니다. 액션객체는 보통 ' type ' 프로퍼티를 가지고,상태값을 업데이트

하는데 사용됩니다.

 

카운터 컴포넌트를 통한 예시를 알아보져

 

import React,{useReducer} from 'react'

function reducer(state,action){

	switch(action.type){
    	case 'INCREMENT' :
        return {count: state.count + 1}
        case 'DECREMENT' :
        return {count: state.count - 1}
        default :
        throw new Error()
    }
}


function Counter(){

	const [state,dispatch] = useReducer(reducer,{counter:0})
    
    return (
    	<div>
        	<p>Count : {state.count}</p>
            <button onClick={()=>dispatch({type:'INCREMENT'})}> Increment </button>
			<button onClick={()=>dispatch({type:'DECREMENT'})}> Decrement </button>
        </div>
    )
    
}

 

위의 코드에서, 'reducer' 함수는 현재 상태 값 'state'와 'action' 객체를 받아서 새로운

상태값을 반환하는 함수입니다.

Counter 컴포넌트에서는 'useReducer'를 사용하여 state와 dispatch를 가져옵니다.

dispatch 함수는 action 객체를 받아서 상태값을 업데이트하는 역할을 합니다.

 

Counter 컴포넌트에서는 dispatch를 함수를 이용해서 INCREMENT 와 DECREMENT

액션을 보내면서 상태값을 업데이트 합니다.

 

 


번외로 dispatch와 reducer함수에대해 조금 이해가 부족한것 같아

설명을 덧붙힙니다.

 

1. dispatch 

 

- reducer 함수를 실행시킵니다.

- action 객체를 인자로 받으며, action객체는 어떤행동인지

  나타내는 type 속성과 해당 행동과 관련된 데이터(payload)를 담고있다.

 

- action을 이용하여 컴포넌트 내에서 state의 업데이트를 일으킨다..

 

ex 1) action type만 정의하여 사용

<button onClick={() => dispatch({ type: "INCREMENT" })}>증가</button>

ex 2) action type과, 데이터를 정의하여 사용

<button onClick={() => dispatch({ type: "INCREMENT", payload: 1 })}>증가</button>

 

2. reducer

 

- 위의 dispatch 함수에 의해 실행되며, 컴포넌트 외부에서 state를 업데이트하는 로직을 담당

- 함수의 인자로는 state와 action을 받게된다.

- state와 action을 활용하여 새로운 state를 반환한다.

 

ex1) action type만 정의하여 사용

function reducer(state,action){

	switch (action.type){
    
    	case 'INCREMENT':
        return {count: state.count + 1}
        
        case 'DECREMENT' :
        return {count: state.count -1}
        
        default :
        throw new Error("Undefined action type : ", action.type)
    }
}

ex2) action type과, 데이터를 정의하여 사용

function reducer(state,action){

	switch(action.type){
    
    	case 'INCREMENT' :
        return {count:state.count + action.payload}
        
        case 'DECREMENT' :
        return {count:state.count - action.payload}
        
        default :
        throw new Error("Undefined action type : ", action.type)
    }
}

 

 

공부하면서 여러 블로그를 참고하여 작성하였습니다.

저도 언젠가는 능수능란하게 상태관리를 잘하는

개발자가...되고싶습니다...!