7일차(6.1~6.5)

01. 리덕스에서 사용되는 키워드 숙지하기

액션 (Action)

  • 상태에 어떠한 변화가 필요하게 될 때 액션이란 것을 발생시킨다
  • 하나의 객체로 표현된다
  • type 필드는 필수, 그 외의 값들은 마음대로 설정할 수 있다
{
  type: "ADD_TODO", // 액션의 이름(필수)
  data: {           // 상태 변화 시 필요한 값
    id: 0,
    text: "리덕스 배우기"
  }
}

액션 생성 함수 (Action Creator)

  • 액션을 만드는 함수
  • 파라미터를 받아와서 액션 객체 형태로 만든다
  • 단지 컴포넌트에서 더욱 간편하게 액션을 발생시키기 위해 생성
  • 보통 함수 앞에 export 키워드를 붙여서 다른 파일에서 불러와서 사용한다
export function addTodo(data) {
  return {
    type: 'ADD_TODO', // 함수 이름과 같은 이름
    data, // 전달된 파라미터 값
  }
}

리듀서 (Reducer)

  • 상태 변화로 로직을 작성해둔 함수
  • 상태와, 액션 객체 두개의 파라미터를 받아온다
  • 현재의 상태전달 받은 액션을 참고하여 새로운 상태를 만들어서 반환한다
  • default: 부분에 기존 상태를 그대로 반환하도록 작성해야한다
  • 여러 개의 리듀서(서브 리듀서)를 만들고 이를 합쳐서 루트 리듀서를 만들 수 있다
function counter(state, action) {
  switch (action.type) {
    case 'INCREASE':
      return state + 1
    case 'DECREASE':
      return state - 1
    default:
      return state
  }
}

스토어 (Store)

  • 한 애플리케이션당 하나의 스토어를 만들 수 있다
  • 상태와, 리듀서, 몇가지 내장 함수들이 포함된다.

디스패치 (dispatch)

  • 스토어의 내장함수 중 하나
  • 액션을 발생시키는 함수
  • 액션 객체를 파라미터로 전달
  • 호출 시 스토어는 리듀서 함수를 실행시켜서 해당 액션을 처리하는 로직이 있다면 액션을 참고하여 새로운 상태를 만들어준다

02. 리덕스의 3가지 규칙

1. 하나의 애플리케이션 안에는 하나의 스토어가 있다

2. 상태는 읽기 전용이다

  • 기존의 상태는 건드리지 않고 새로운 상태를 생성하여 업데이트 해주면,
    나중에 개발자 도구를 통해서 뒤로 돌릴 수도 있고 다시 앞으로 돌릴 수 있다
  • 불변성을 유지해야하는 이유
  • 객체의 변화를 감지 할 때 shallow equality 검사를 해, 겉핥기 식으로 비교를 하기 때문
  • 이로써 좋은 성능을 유지할 수 있다
    안쪽까지 검사를 안하기 때문에 겉은 같고 내부만 바꿔봤자 변한지 모른다
    그래서 겉까지 싹다 바꿔줘야 함

3. 변화를 일으키는 함수, 리듀서는 순수한 함수여야 한다

  • 리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받는다
  • 이전의 상태는 절대로 건드리지 않고, 변화를 일으킨 새로운 상태 객체를 만들어서 반환한다
  • 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과값을 반환해야만 한다
    → new Date(), 랜덤 숫자 생성, 네트워크 요청 등 실행 시마다 다른 결과를 내는 로직은 사용하면 안된다

03. 리덕스 사용할 준비하기

코드 작성


04. 리덕스 모듈 만들기

리덕스 모듈 - Ducks 패턴

  • 액션 타입
  • 액션 생성함수
  • 리듀서

이 모두 포함된 js파일(각각 다른 파일에 저장 할 수도 있다)

1. 리듀서 생성하기

/* **액션 타입 만들기** */
액션의 이름에 접두사를 넣어야 중복을 방지할 수 있다

/* **액션 생성함수 만들기** */
export사용해 내보내준다

/* **초기 상태 선언** */

/* **리듀서 선언** */
export default로 내보낸다

여러 리듀서 합치기

  • 한 프로젝트에 여러개의 리듀서가 있을때는 이를 한 리듀서로 합쳐서 사용한고 합쳐진 리듀서를 루트 리듀서라고 부른다
  • 리덕스에 내장되어있는 [combineReducers](https://redux.js.org/api/combinereducers)라는 함수를 사용
  • index.js파일에서 합쳐준다
import { combineReducers } from 'redux'
import counter from './counter'
import todos from './todos'

const rootReducer = combineReducers({
  counter,
  todos,
})

export default rootReducer

2. 스토어 만들기

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import * as serviceWorker from './serviceWorker'

import { createStore } from 'redux'
import rootReducer from './modules'

const store = createStore(rootReducer) // 스토어를 만듭니다
console.log(store.getState()) // 스토어의 상태 확인

ReactDOM.render(<App />, document.getElementById('root'))

serviceWorker.unregister()

3. 리액트 프로젝트에 리덕스 적용하기

리액트 프로젝트에 리덕스를 적용 할 때, react-redux라는 라이브러리의 Provider컴포넌트를 사용

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import * as serviceWorker from './serviceWorker'

import { createStore } from 'redux'
import rootReducer from './modules'
import { Provider } from 'react-redux' // Provier컴포넌트를 꺼내옴

const store = createStore(rootReducer)

ReactDOM.render(
  <Provider store={store}>
    {' '}
    // App에 감싼 후 store를 전달해주면 어떤 컴포넌트던지 스토어에 접근할 수 있다
    <App />
  </Provider>,
  document.getElementById('root')
)

serviceWorker.unregister()

05. 카운터 구현하기

프리젠테이셔널 컴포넌트

리덕스 스토어에 직접적으로 접근하지 않고 필요한 값 또는 함수를 props으로만 받아와서 사용하는 컴포넌트

  • UI를 선언하는 것에 집중
  • 필요한 값들이나 함수는 props 로 받아와서 사용

컨테이너 컴포넌트

리덕스 스토어의 상태를 조회하거나, 액션을 디스패치 할 수 있는 컴포넌트를 의미

  • HTML 태그들을 사용하지 않는다
  • 다른 프리젠테이셔널 컴포넌트들을 불러와서 사용한다
  • page같은 느낌

06. 리덕스 개발자도구 적용하기

  • 현재 스토어의 상태 조회
  • 어떤 액션들이 디스패치 되었는지 확인 가능
  • 액션에 따라 상태가 어떻게 변화했는지 확인 가능
  • 액션을 직접 디스패치 할 수도 있다

yarn add redux-devtools-extension

index.js

import { composeWithDevTools } from 'redux-devtools-extension'


07. 할 일 목록 구현하기

  • TodosContainer → redux에서 값 받아옴
  • Todos
    • TodoList
      • TodoItem

props로 쭉쭉 내려가면서 사용


08. useSelector 최적화

객체로 만들어주게되면 렌더링될 때마다 새로운 객체를 만드는 것이기 때문에 내부의 요소(상태)가 바뀌었는지 아닌지 확인을 할 수 없어서 낭비 렌더링이 이루어진다

const { number, diff } = useSelector(state => ({
  number: state.counter.number,
  diff: state.counter.diff
}));

최적화하기 위한 방법

  1. 객체로 만들지 말고 각각 만들어준다
const number = useSelector(state => state.counter.number);
const diff = useSelector(state => state.counter.diff);
  1. react-redux의 shallowEqual 함수를 useSelector의 두번째 인자로 전달해준다
import React from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';

function CounterContainer() {
  const { number, diff } = useSelector(
    state => ({
      number: state.counter.number,
      diff: state.counter.diff
    }),
    shallowEqual // 객체 안의 가장 겉에 있는 값들을 모두 비교해준다
  );

  (...)

출처 https://react.vlpt.us/

좋은 웹페이지 즐겨찾기