Javascript/React

[React/Redux] 🛡 immer으로 배열 내 특정 값 변경하기 (불변성 절대 지켜!)

제로초의 Nodebird 강의 중, immer 라이브러리를 사용해보았다.

find 함수와 immer을 함께 사용했을때 신기한 경험을 해서 기록해둔다.

 

immer란?
reduce state 불변성을 지켜주는 편리한 라이브러리다.

 

목표
post.mainPosts 배열에서 특정 id를 가진 값을 찾아, post.mainPosts[index].Comments배열에 값을 추가하려고 한다.
const reducer = (state = initialState, action) =>
    produce(state, draft => {
        switch (action.type) {
            case ADD_COMMENT_SUCCESS: {
                const post = draft.mainPosts.find(v => v.id === action.data.postId);
                post.Comments.unshift(action.data);

                draft.addCommentLoading = false;
                draft.addCommentDone = true;
                draft.addCommentError = false;
                break;
            }
        }
    })

 

const post = draft.mainPosts.find(v => v.id === action.data.postId);
post.Comments.unshift(action.data);

 

위 코드에서 post 변수는 draft.mainPosts 배열에서 id가 action.data.postId와 일치하는 요소를 찾는다.

그리고 post.Comments.unshift(action.data)를 사용하여 해당 post의 Comments 배열의 앞쪽에 새로운 데이터인 action.data를 추가한다.

이렇게 하면 draft 객체 내의 mainPosts 상태가 변경된다고 한다!

 

post에는 find함수로 리턴된 mainPost객체 한개만 담겨 있을 것이라 생각했는데,

post에는 draft에서 얕은복사가 된채로 find로 커서가 잡혀있고, unshift로 comment를 추가했다.

 

 


redux공식 문서에서 immer와 함께 사용하는 방법이 예시로 있다.

https://redux-toolkit.js.org/usage/immer-reducers#updating-nested-data

 

Writing Reducers with Immer | Redux Toolkit

 

redux-toolkit.js.org

 

Immer greatly simplifies updating nested data. Nested objects and arrays are also wrapped in Proxies and drafted, and it's safe to pull out a nested value into its own variable and then mutate it.

However, this still only applies to objects and arrays. If we pull out a primitive value into its own variable and try to update it, Immer has nothing to wrap and cannot track any updates.

Immer는 중첩된 데이터를 간단하게 업데이트할 수 있도록 도와줍니다. 

중첩된 객체와 배열도 프록시로 래핑되고 수정 가능하게 되며, 중첩된 값을 개별 변수로 추출한 다음 직접 수정하는 것도 안전합니다. 

하지만 이는 객체와 배열에만 적용됩니다. 

만약 우리가 원시 값(Primitive value)을 개별 변수로 추출하고 업데이트를 시도한다면, Immer는 래핑할 대상이 없으므로 어떤 업데이트도 추적할 수 없습니다.

const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    brokenTodoToggled(state, action) {
      const todo = state.find((todo) => todo.id === action.payload)
      if (todo) {
        // ❌ ERROR: Immer can't track updates to a primitive value!
        let { completed } = todo
        completed = !completed
      }
    },
    fixedTodoToggled(state, action) {
      const todo = state.find((todo) => todo.id === action.payload)
      if (todo) {
        // ✅ CORRECT: This object is still wrapped in a Proxy, so we can "mutate" it
        todo.completed = !todo.completed
      }
    },
  },
})

 


 

관련한 공식문서를 보면 더 다양한 방법을 볼 수 있다.

https://immerjs.github.io/immer/update-patterns/

 

Update patterns | Immer

Working with immutable data, before Immer, used to mean learning all the immutable update patterns.

immerjs.github.io

 

#draft find unshift 

728x90
반응형