programing

특정 어레이 항목 내의 단일 값을 redx로 업데이트하는 방법

mbctv 2023. 4. 1. 09:53
반응형

특정 어레이 항목 내의 단일 값을 redx로 업데이트하는 방법

상태 재렌더로 인해 UI 문제가 발생하는 문제가 있으며 페이지 재렌더 양을 줄이기 위해 리듀서 내에서 특정 값만 업데이트하도록 제안받았습니다.

이것은 내 상태의 예시이다.

{
 name: "some name",
 subtitle: "some subtitle",
 contents: [
   {title: "some title", text: "some text"},
   {title: "some other title", text: "some other text"}
 ]
}

그리고 현재 이렇게 업데이트 하고 있습니다.

case 'SOME_ACTION':
   return { ...state, contents: action.payload }

서 ''는action.payload는 새로운 값을 포함하는 배열 전체입니다.의 두 하지 않습니다.이러한 것은 동작하지 않습니다.

case 'SOME_ACTION':
   return { ...state, contents[1].text: action.payload }

서 ''는action.payload업데이트에 필요한 텍스트가 되었습니다.

하시면 됩니다.map하다

case 'SOME_ACTION':
   return { 
       ...state, 
       contents: state.contents.map(
           (content, i) => i === 1 ? {...content, text: action.payload}
                                   : content
       )
    }

React 불변성 도우미를 사용할 수 있습니다.

import update from 'react-addons-update';

// ...    

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      1: {
        text: {$set: action.payload}
      }
    }
  });

아마 이런 일을 더 하고 있을 거라고 생각하겠지만요?

case 'SOME_ACTION':
  return update(state, { 
    contents: { 
      [action.id]: {
        text: {$set: action.payload}
      }
    }
  });

파티에는 늦었지만 여기 모든 인덱스 값에 적용되는 일반적인 솔루션이 있습니다.

  1. 하여 「」까지 합니다.index변화하고 싶겠지

  2. 원하는 데이터를 추가합니다.

  3. 펼칩니다.index.

let index=1;// probably action.payload.id
case 'SOME_ACTION':
   return { 
       ...state, 
       contents: [
          ...state.contents.slice(0,index),
          {title: "some other title", text: "some other text"},
         ...state.contents.slice(index+1)
         ]
    }

업데이트:

코드를 간단하게 하기 위해 작은 모듈을 만들었기 때문에 함수를 호출하기만 하면 됩니다.

case 'SOME_ACTION':
   return {
       ...state,
       contents: insertIntoArray(state.contents,index, {title: "some title", text: "some text"})
    }

자세한 예는 저장소를 참조해 주세요.

함수 시그니처:

insertIntoArray(originalArray,insertionIndex,newData)

편집: Immer.js 라이브러리도 있어 모든 종류의 값을 사용할 수 있으며 깊이 중첩할 수도 있습니다.

모든 작업을 한 줄로 수행할 필요는 없습니다.

case 'SOME_ACTION': {
  const newState = { ...state };
  newState.contents = 
    [
      newState.contents[0],
      {title: newState.contents[1].title, text: action.payload}
    ];
  return newState
};

저는 당신이 Redux에 대해 이런 종류의 수술이 필요할 때 스프레드 오퍼레이터는 당신의 친구이며 이 교장은 모든 아이들에게 적용된다고 믿습니다.

예를 들어 다음과 같이 가정합니다.

const state = {
    houses: {
        gryffindor: {
          points: 15
        },
        ravenclaw: {
          points: 18
        },
        hufflepuff: {
          points: 7
        },
        slytherin: {
          points: 5
        }
    }
}

그리고 레이븐클로에 3점을 더하고 싶으시다고요?

const key = "ravenclaw";
  return {
    ...state, // copy state
    houses: {
      ...state.houses, // copy houses
      [key]: {  // update one specific house (using Computed Property syntax)
        ...state.houses[key],  // copy that specific house's properties
        points: state.houses[key].points + 3   // update its `points` property
      }
    }
  }

확산 연산자를 사용하면 다른 모든 것은 그대로 두고 새 상태만 업데이트할 수 있습니다.

놀라운 기사에서 예를 들면, 가능한 거의 모든 옵션과 훌륭한 예를 찾을 수 있습니다.

이것은 redux-toolkit에서는 매우 간단합니다.Immer를 사용하여 보다 간결하고 읽기 쉬운 변이 가능한 것처럼 보이는 불변의 코드를 쓸 수 있습니다.

// it looks like the state is mutated, but under the hood Immer keeps track of
// every changes and create a new state for you
state.x = newValue;

따라서 일반 환원기에서는 확산 연산자를 사용하는 대신

return { 
  ...state, 
  contents: state.contents.map(
      (content, i) => i === 1 ? {...content, text: action.payload}
                              : content
  )
}

로컬 값을 재할당하고 나머지는 Immer에 맡기면 됩니다.

state.contents[1].text = action.payload;

라이브 데모

35628774/업데이트 방법-단일값-내부특정-어레이-항목-인-redux 편집

저 같은 경우에는 루이스의 대답에 기초하여 다음과 같은 일을 했습니다.

// ...State object...
userInfo = {
name: '...',
...
}

// ...Reducer's code...
case CHANGED_INFO:
return {
  ...state,
  userInfo: {
    ...state.userInfo,
    // I'm sending the arguments like this: changeInfo({ id: e.target.id, value: e.target.value }) and use them as below in reducer!
    [action.data.id]: action.data.value,
  },
};

Immer.js(놀라운 리액트/rn/redux 친화 패키지)는 이 문제를 매우 효율적으로 해결합니다.Redux 저장소는 불변의 데이터로 구성됩니다.immer를 사용하면 저장된 데이터를 불변의 데이터가 아닌 것처럼 깔끔하게 코딩하여 업데이트할 수 있습니다.

다음은 redex에 대한 설명서의 예시입니다. (메서드에 둘러싸인 product()에 주의해 주세요.리듀서 설정의 변경은 이것뿐입니다.)

import produce from "immer"

// Reducer with initial state
const INITIAL_STATE = [
    /* bunch of todos */
]

const todosReducer = produce((draft, action) => {
    switch (action.type) {
        case "toggle":
            const todo = draft.find(todo => todo.id === action.id)
            todo.done = !todo.done
            break
        case "add":
            draft.push({
                id: action.id,
                title: "A new todo",
                done: false
            })
            break
        default:
            break
    }
})

(리듀스 툴킷의 부작용으로 immer를 언급했지만 리듀서에 immer를 직접 사용해야 합니다.)

Immer 설치 : https://immerjs.github.io/immer/installation

프로젝트 중 하나를 위해 이렇게 했습니다.

const markdownSaveActionCreator = (newMarkdownLocation, newMarkdownToSave) => ({
  type: MARKDOWN_SAVE,
  saveLocation: newMarkdownLocation,
  savedMarkdownInLocation: newMarkdownToSave  
});

const markdownSaveReducer = (state = MARKDOWN_SAVED_ARRAY_DEFAULT, action) => {
  let objTemp = {
    saveLocation: action.saveLocation, 
    savedMarkdownInLocation: action.savedMarkdownInLocation
  };

  switch(action.type) {
    case MARKDOWN_SAVE:
      return( 
        state.map(i => {
          if (i.saveLocation === objTemp.saveLocation) {
            return Object.assign({}, i, objTemp);
          }
          return i;
        })
      );
    default:
      return state;
  }
};

유감입니다만약에map()어레이 전체를 반복하기 때문에 어레이 방식은 비용이 많이 들 수 있습니다.대신 다음 세 부분으로 구성된 새 어레이를 결합합니다.

  • head - 수정된 항목 이전 항목
  • 수정 항목
  • tail - 수정된 항목 뒤의 항목

다음은 코드에서 사용한 예(NgRx, 그러나 기계주의는 다른 Redux 구현에서도 동일)입니다.

// toggle done property: true to false, or false to true

function (state, action) {
    const todos = state.todos;
    const todoIdx = todos.findIndex(t => t.id === action.id);

    const todoObj = todos[todoIdx];
    const newTodoObj = { ...todoObj, done: !todoObj.done };

    const head = todos.slice(0, todoIdx - 1);
    const tail = todos.slice(todoIdx + 1);
    const newTodos = [...head, newTodoObj, ...tail];
}

데이터 구조에 주의해 주세요.프로젝트에서는, 다음과 같은 데이터가 있어, 1개아이템을 갱신하기 위해서, 이 작업을 실시합니다.

case actionTypes.UPDATE_COMMENT:
  const indexComment = state.comments.items.findIndex( 
    (comment) => comment.id === action.payload.data.id,
  );
  return {
    ...state,
    comments: {
      ...state.comments,
      items: state.comments.items.map((el, index) =>
        index === indexComment ? { ...el, ...action.payload.data } : el,
      ),
    },
  };

주의: 새로운 버전 (@reduxjs/toolkitRedux는 오브젝트 변경을 자동으로 검출하므로 완전한 상태를 반환할 필요가 없습니다.

/* reducer */
const slice = createSlice({
  name: 'yourweirdobject',
  initialState: { ... },
  reducers: {
    updateText(state, action) {
      // updating one property will cause Redux to update views
      // only depending on that property.
      state.contents[action.payload.id].text = action.payload.text
    },
    ...
  }
})

/* store */
export const store = configureStore({
  reducer: {
    yourweirdobject: slice.reducer
  }
})

지금은 이렇게 해야 돼.

언급URL : https://stackoverflow.com/questions/35628774/how-to-update-single-value-inside-specific-array-item-in-redux

반응형