개발/frontend

Redux) redux-tool kit createEntityAdapter 설명

체인의정석 2025. 7. 10. 17:37
728x90

createEntitiy Adapter의 경우 미리 준비된 reducer (함수들)을 탑재하고 있는 객체이다.

  • addOne / addMany: add new items to the state
  • upsertOne / upsertMany: add new items or update existing ones
  • updateOne / updateMany: update existing items by supplying partial values
  • removeOne / removeMany: remove items based on IDs
  • setAll: replace all existing items

위와 같이 state를 바꿔 줄 수 있는 reducers들을 내장하고 있다.

https://ko.redux.js.org/tutorials/fundamentals/part-8-modern-redux/#using-createentityadapter

 

Redux 기반, Part 8: Modern Redux with Redux Toolkit | Redux

The official Fundamentals tutorial for Redux: learn the modern way to write Redux logic

ko.redux.js.org

import {
  createEntityAdapter,
  createSlice,
  configureStore,
} from '@reduxjs/toolkit'

type Book = { bookId: string; title: string }

const booksAdapter = createEntityAdapter({
  // Assume IDs are stored in a field other than `book.id`
  selectId: (book: Book) => book.bookId,
  // Keep the "all IDs" array sorted based on book titles
  sortComparer: (a, b) => a.title.localeCompare(b.title),
})

const booksSlice = createSlice({
  name: 'books',
  initialState: booksAdapter.getInitialState(),
  reducers: {
    // Can pass adapter functions directly as case reducers.  Because we're passing this
    // as a value, `createSlice` will auto-generate the `bookAdded` action type / creator
    bookAdded: booksAdapter.addOne,
    booksReceived(state, action) {
      // Or, call them as "mutating" helpers in a case reducer
      booksAdapter.setAll(state, action.payload.books)
    },
  },
})

const store = configureStore({
  reducer: {
    books: booksSlice.reducer,
  },
})

type RootState = ReturnType<typeof store.getState>

console.log(store.getState().books)
// { ids: [], entities: {} }

// Can create a set of memoized selectors based on the location of this entity state
const booksSelectors = booksAdapter.getSelectors<RootState>(
  (state) => state.books,
)

// And then use the selectors to retrieve values
const allBooks = booksSelectors.selectAll(store.getState())

위는 예시 코드이다. 

createEntityAdapter의 경우 하나의 Entitiy 인스턴스를 입력값으로 받는다. 만약 파라미터가 안 들어 있다면 기본 값으로 entitiy.id가 생성되게 된다. 만약 아니라면 selectId 함수를 따로 넣어서 Entitiy를 지정해 주는 과정이 필요하다.

{
  // The unique IDs of each item. Must be strings or numbers
  ids: []
  // A lookup table mapping entity IDs to the corresponding entity objects
  entities: {
  }
}

adapter가 생성이 되면 위와 같이 생긴 entitiy state를 조작하는 형태로 관리가 가능하다.

getSelector 함수의 경우 entity adapter가 포함하고 있는 함수라고 한다. state를 포함하고 있는객체에 직접 접근하는 기능을 해당 내장 함수에서 한다고 보면 된다. 

get selector에 콜을 하는 방법은 크게 2가지 인데

const store = configureStore({
  reducer: {
    books: booksReducer,
  },
})

const simpleSelectors = booksAdapter.getSelectors()
const globalizedSelectors = booksAdapter.getSelectors((state) => state.books)

// Need to manually pass the correct entity state object in to this selector
const bookIds = simpleSelectors.selectIds(store.getState().books)

// This selector already knows how to find the books entity state
const allBooks = globalizedSelectors.selectAll(store.getState())

selector를 만드는 방식에 따라서 나중에 활용법도 위와 같이 2가지로 갈리게 된다. 

그리고 adapter에 있는 데이터를 조작하는 부분도 아래와 같이 adapter.addOne 이런식으로 활용이 가능하다. 내가 다루고 잇는 프로젝트에서도 동일하게 adapter를 통하여서 테이블 형태의 데이터를 관리하고 있었다.

  • addOne and addMany will do nothing with the new entity
  • setOne and setMany will completely replace the old entity with the new one. This will also get rid of any properties on the entity that are not present in the new version of said entity.
  • upsertOne and upsertMany will do a shallow copy to merge the old and new entities overwriting existing values, adding any that were not there and not touching properties not provided in the new entity.
728x90
반응형