[React] Redux Toolkit 리덕스 툴킷 상태관리

gigi·2022년 7월 11일
0

리덕스 툴킷 설치

npm install @reduxjs/toolkit react-redux

리덕스 스토어 생성

src/app/store.js 파일을 만든다.

해당파일에서 Redux Toolkit의 { configureStore } API를 가져온다 .

src/app/store.js

  • TypeScript
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
  • JavaScript
import { configureStore } from '@reduxjs/toolkit'

export const store = configureStore({
  reducer: {},
})

이렇게 하면 Redux 저장소가 생성되고 개발하는 동안 저장소를 검사할 수 있도록 Redux DevTools 확장도 자동으로 구성된다.


리액트에 리덕스 스토어 제공

index.js 파일에서 Store, Provider 를 import 해오고 <App />을 <Provider>로 감싸 store를 props로 전달해준다.

index.js

  • TypeScript / JavaScript
import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import { store } from './app/store'
import { Provider } from 'react-redux'

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

리덕스 State Slice 만들기

src/features/counter/counterSlice.js 파일을 만든다.

해당 파일에서 Redux Toolkit의 { createSlice } API를 가져온다.

슬라이스를 생성하려면 슬라이스를 식별하기 위한 문자열 이름, 초기 상태 값, 상태 업데이트 방법을 정의하는 하나 이상의 리듀서 함수가 필요하다.

슬라이스가 생성되면 생성된 Redux 액션 생성자와 전체 슬라이스에 대한 리듀서 기능을 내보낼 수 있다.

src/features/counter/counterSlice.js

  • TypeScript
import { createSlice, PayloadAction } from '@reduxjs/toolkit'

export interface CounterState {
  value: number
}

const initialState: CounterState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<number>) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer
  • JavaScript
import { createSlice } from '@reduxjs/toolkit'

const initialState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

Redux는 데이터 복사본을 만들고 복사본을 업데이트하여 모든 상태 업데이트를 불변하게 작성하도록 요구한다. 그러나 Redux 툴킷의 createSlice 및 createReducer API는 Immer 내부를 사용하여 올바른 변경 불가능한 업데이트가 "변경되는" 업데이트 로직을 작성할 수 있게한다.


스토어에 슬라이스 리듀서 추가

다음으로 카운터 슬라이스에서 리듀서 함수를 가져와서 스토어에 추가해야 한다. 리듀서 매개변수 내부에 필드를 정의함으로써 스토어에 이 슬라이스 리듀서 함수를 사용하여 해당 상태에 대한 모든 업데이트를 처리하도록 지시한다.

src/app/store.js

  • TypeSciprt
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
  • JavaScript
import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch

리액트 구성 요소에서 리덕스 상태 및 작업 사용

이제 React-Redux Hook를 사용하여 React 구성 요소가 Redux 저장소와 상호 작용하도록 할 수 있다.

useSelector를 사용하여 저장소에서 데이터를 읽고 useDispatch를 사용하여 작업을 전달할 수 있다.

내부에 <Counter> 구성 요소가 있는 src/features/counter/Counter.js 파일을 만든 다음 해당 구성 요소를 App.js로 가져오고 <App> 내부에서 렌더링해보자.

src/features/counter/Counter.js

  • TypeScript
import React from 'react'
import { RootState } from '../../app/store'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state: RootState) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}
  • JavaScript
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { decrement, increment } from './counterSlice'

export function Counter() {
  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  )
}

이제 "증가" 및 "감소" 버튼을 클릭할 때마다

  • 해당 Redux 작업이 스토어로 발송된다.
  • 카운터 슬라이스 리듀서는 작업을 보고 상태를 업데이트한다.
  • <Counter> 구성 요소는 저장소의 새 상태 값을 보고 새 데이터로 자체를 다시 렌더링한다.

0개의 댓글