react - 상태 관리 2(Redux 연습, 공식 문서 탐구)

2021. 11. 2. 18:00·SE Bootcamp 내용 정리

Redux 연습하기

상품 리스트 페이지와 장바구니 페이지로 단순화해서 만들기

// 구조를 살펴보면 다음과 같다

index.js – App.js    - Nav.js
            - ItemListContainer.js    - Item.js
            - ShoppingCart.js        - CartItem.js
                            - OrderSummary.js


    state: { itemList: […], cartItemList: […], ...}

App.js에 모든 state가 있는 상황에서 장바구니의 물건을 업데이트하려면?
→ 수많은 props drilling이 발생함!

 

→ 전역 상태를 담고 있는 Store가 있다면 해결 가능

Cmarket Shopping App

Create React App으로 만든 리액트 앱에 Redux를 붙인 구조의 앱

// 구조
* 아이템 리스트 페이지(ItemListContainer)와 장바구니 페이지(ShoppingCart)의 2개의 페이지로 구성
* Store의 initial state에는 전체 아이템 목록(items), 장바구니 목록(cartItmes)로 구성
* 각 페이지 컴포넌트(ItemListContainer,  ShoppingCart)와 components 폴더의 
여러 컴포넌트에서 Store(state)에 접근이 필요(Redux의 hooks, useDispatch, useSelector 활용)

 

위의 구조를 기반으로 하여 해당 app을 구현한 코드

https://github.com/racyde/im-sprint-cmarket-redux/tree/master/src

Action

말 그대로 어떤 액션(행동, 이벤트)를 할 것인지 정의해 놓은 객체(Object)이다

//예시
{ 
    type: ‘ADD_TO_CART’, 
        payload: request 
}

type은 필수로 지정(일종의 별명)해 줘야 하며, 나머지는 선택적 부분

 

→ Action을 통해 변화하는 부분을 직관적으로 알기 쉽게 해 준다

Dispatch

Dispatch는 Action을 전달하는 메소드
→ dispatch()의 전달 인자로 Action 객체가 전달된다

 

그리고 Reducer를 호출하여 state의 값을 바꾸는 역할을 함

Store

말 그대로 state가 관리되는 오직 하나 뿐인 저장소의 역할
→ Redux 앱의 state가 저장되어 있는 공간

 

보통, 구성 컴포넌트들 전체에서 사용할 수 있는 Store를 만들기 위해 전체 애플리케이션을 <Provider> 컴포넌트로 감싸면서 시작해야 한다
→ index.js 와 같은 파일에서 코드 작성 필요한 듯?

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

Reducer

Reducer는 현재의 state와 Action을 이용해서 새로운 state를 만들어 내는 순수 함수(pure function) 이다

// Reducer의 예시

const itemReducer = (state = initialState, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      return Object.assign({}, state, {
        cartItems: [...state.cartItems, action.payload]
      })

…(생략)

    default:
      return state;
  }
}

// if 문 같은 다른 조건문으로 작성해도 무방함

Reducer의 Immutablility(불변성)

Redux의 state 업데이트는 immutable한 방식으로 변경해야 한다!

Redux의 장점인 변경된 state를 로그로 남기기 위해서 꼭 필요한 작업

 

immutable 하게 state를 변경하기 위해서는 immutable한 메서드를 사용하거나 하는 등의 방법으로, 원본 값을 직접적으로 건드리지 않게만 잘 코딩하면 된다
→ 위의 예시에서 Object.assign 메서드(immutable)을 사용한 것처럼 말이다

Redux Hooks

Action, Reducer, Dispatch, Store의 개념들을 연결(connect)시키기 위한 방법 중의 하나이다
(다른 하나는 connect parameter를 이용하는 방법)
→ Redux Hooks가 비교적 사용이 쉽고, 최근에 나온 방식

 

Redux 공식 문서를 통해 내용을 정리하였다
https://react-redux.js.org/api/hooks

 

Hooks | React Redux

API > Hooks: the `useSelector` and `useDispatch` hooks`

react-redux.js.org

useSelector()

useSelector()는 컴포넌트와 state를 연결하는 역할
→ 컴포넌트에서 useSelector 메소드를 통해 store의 state에 접근

 

어떤 컴포넌트에서 useSelector를 사용해야 할지 고민해보자

 

useSelector의 전달인자: 콜백 함수
→ 그 콜백 함수의 전달인자로는 state 값이 들어감

// 기본 구조
const result: any = useSelector(selector: Function, equalityFn?: Function)

작동 로직

함수 컴포넌트가 렌더링될 때, 선언한 selector 함수가 호출되고 그 결과가 useSelector() hook에서 반환된다(캐시된 결과는 구성 요소의 이전 렌더링과 동일한 함수 참조인 경우 selector를 다시 실행하지 않고 hook에 의해 반환될 수 있다)

 

Action이 Redux Store로 dispatch될 때, selector의 결과가 마지막 결과와 다른 경우에만, useSelector()가 강제로 다시 렌더링한다

 

기본적으로 비교는 참조 값과의 엄격한 비교(===) 이다

 

useSelector()를 사용하면 새로운 객체를 반환할 때 마다 기본적으로 다시 렌더링된다

 

Store에서 여러 value값들을 찾길 원한다면, 다음과 같이 할 수 있다

 

* 하나의 필드 값을 반환하는 호출마다 useSelector()를 호출하기

* `Reselect` 또는 이와 유사한 라이브러리를 사용하여, 하나의 객체 안의 여러 값(value)를 반환하지만 
그 value 값 중 하나가 변경되었을 때만 새 객체를 반환하는 메모형 Selector 만들기

* `shallowEqual` 함수를 useSelector() 안의 인자(equalityFn 자리;2번째 인자 자리)로 사용

// 활용 예시

import { shallowEqual, useSelector } from 'react-redux'

// later
const selectedData = useSelector(selectorReturningObject, shallowEqual)

 

아래는 기본적인 useSelector() 사용 형태이다

// 기본 사용 형태

import React from 'react'
import { useSelector } from 'react-redux'

export const CounterComponent = () => {  // 특정 컴포넌트 내에서

// useSelector() 사용 구문
    const counter = useSelector((state) => state.counter)
                // useSelector의 전달인자로 콜백함수 (state) => state.counter
                // 그 콜백 함수의 전달인자는 state

    …
//리턴 부분
    return <div>{counter}</div>
}

 

props를 전달받는 경우의 useSelector() 사용은?

import React from 'react'
import { useSelector } from 'react-redux'

export const TodoListItem = (props) => {  // 컴포넌트에서 props를 전달 받아서

// useSelector() 사용 구문
    const todo = useSelector((state) => state.todos[props.id])  


    …
//리턴 부분
    return <div>{todo.text}</div>
}

useDispatch()

Action 객체를 Reducer로 전달해주는 메소드

 

어떤 컴포넌트에서 useDispatch()를 이용해 Action을 Reducer로 전달해줄 수 있을지 고민이 필요

// 기본 사용 형태

import React from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {  
    const dispatch = useDispatch()  // useDispatch() 사용


…
// 리턴 부분
    return (
        <div>
          <span>{value}</span>
          <button onClick={() => dispatch({ type: 'increment-counter' })}>
          	Increment counter
          </button>
        </div>
      )
}

 

자식 컴포넌트에 dispatch를 사용하여 콜백 함수를 전달할 때, useCallback을 사용하여 메모라이징할 수 있다.
자식 컴포넌트에서도 렌더링을 최적화하기 위해, React.memo()를 사용하여 콜백 함수 레퍼런스의 변경에 따른 불필요한 렌더링을 피할 수 있다

import React, { useCallback } from 'react'
import { useDispatch } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const dispatch = useDispatch()
  const incrementCounter = useCallback(
    () => dispatch({ type: 'increment-counter' }),
    [dispatch]
  )


  return (
    <div>
      <span>{value}</span>
      <MyIncrementButton onIncrement={incrementCounter} />
    </div>
  )
}

export const MyIncrementButton = React.memo(({ onIncrement }) => (
  <button onClick={onIncrement}>Increment counter</button>
))

useStore()

기본 형태

const store = useStore()

<Provider> 구성 요소에 전달된 동일한 Redux Store에 대한 참조를 반환

이 hook는 자주 사용하지 않아야 한다. 기본적으로 이 hook 보다는 useSelector() hook을 사용하는 것을 추천함

 

대신, Reducer의 교체와 같이 Store에 접근이 필요한 경우에 유용하게 사용할 수 있다

import React from 'react'
import { useStore } from 'react-redux'

export const CounterComponent = ({ value }) => {
  const store = useStore()

  // EXAMPLE ONLY! Do not do this in a real app.
  // The component will not automatically update if the store state changes

  return <div>{store.getState()}</div>
}
저작자표시 (새창열림)

'SE Bootcamp 내용 정리' 카테고리의 다른 글

Linux - 사용 권한과 환경변수  (0) 2021.11.08
클라이언트 빌드와 배포  (0) 2021.11.08
react - 상태 관리 1(기본 상태 관리, Redux)  (0) 2021.11.02
react - 컴포넌트 디자인  (0) 2021.10.28
Web Server - 서버 만들기 연습  (0) 2021.10.28
'SE Bootcamp 내용 정리' 카테고리의 다른 글
  • Linux - 사용 권한과 환경변수
  • 클라이언트 빌드와 배포
  • react - 상태 관리 1(기본 상태 관리, Redux)
  • react - 컴포넌트 디자인
레실이
레실이
  • 레실이
    레실이의 티스토리
    레실이
  • 전체
    오늘
    어제
    • 분류 전체보기 (91)
      • SE Bootcamp 내용 정리 (63)
      • 알고리즘 연습 (7)
      • Project 주저리 (4)
      • 기술 면접 source (3)
      • 개발 일상 (12)
      • 생성 AI 활용 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    mongoDB
    useRef
    node
    state
    인증/보안
    알고리즘
    데이터베이스
    Linux
    Ajax
    문자열
    fastapi
    ubuntu
    CORS
    MVC
    JS
    비동기
    JavaScript
    ORM
    자료구조
    Python
    CSR
    PickAndDrink
    promise
    IT
    node.js
    useState
    react
    DOM
    CSS
    객체
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
레실이
react - 상태 관리 2(Redux 연습, 공식 문서 탐구)
상단으로

티스토리툴바