본문 바로가기

Web + APP/React

[리액트 스터디] - Updating Arrays in State

반응형
SMALL

배열

 

JS에서는 변경 가능하지만, state로 저장할 때에는 변경할 수 없도록 처리해야함.

 

객체랑 마찬가지로, state에 저장된 배열을 업데이트하고 싶을 땐, 새 배열을 생성한 뒤 이를 이용하여 업데이트해야함.

 


학습내용

- React state에서 배열의 항목을 추가, 삭제 또는 변경하는 방법

- 배열 내부의 객체를 업데이트하는 방법

- Immer로 덜 반복해서 배열을 복사하는 방법


변경하지 않고 배열 업데이트하기

JS에서 배열은 객체의 다른 종류임.

 

객체처럼 React state에서는 배열도 읽기 전용이 되어야함.

 

재할당 이런거 하지마셈 (arr[0] = 'test' / push() / pop())

 

 

왜 state 변경 권장하지 않을까 ?

1. 디버깅 힘듦, 렌더링 사이에 state가 어떻게 바뀌었는지 명확하게 알 수 없음
2. 최적화, 리액트 전략이 이전 props / state 가 다음 것과 같을 때 일을 건너뜀
3. 리액트 개발 방향성, 리액트 새 기능을 사용 못 할 수 있음
4. 요구사항 변화, 복원 구현이 힘듦
5. 간단한 구현, 리액트는 변경에 의존하지 않음

걍 하지마

 

state 설정 함수에 새 배열을 넣어서 전달하자.

 

메소드의 구분을 잘하자

 

Immer 쓰면 간단하게 된다고 한다.

 

slice랑 splice 함수는 이름이 비슷하지만 몹시 다름

splice를 사용하면 배열 또는 그 일부를 복사함
splice는 배열을 변경함 (항목을 추가하거나 제거)
slice를 잘 사용하자

배열에 항목 추가하기

push() 쓰지마셈

하지말라고

스프레드 연산자 쓰셈

// 뒤에 넣을 때
setArtists( // 아래의 새로운 배열로 state를 변경합니다.
  [
    ...artists, // 기존 배열의 모든 항목에,
    { id: nextId++, name: name } // 마지막에 새 항목을 추가합니다.
  ]
);

// 앞에 넣을 때
setArtists([
  { id: nextId++, name: name }, // 추가할 항목을 앞에 배치하고,
  ...artists // 기존 배열의 항목들을 뒤에 배치합니다.
]);

배열에서 항목 제거하기

filter 쓰셈.

 

setArtists(
  artists.filter(a => a.id !== artist.id)
);

 

새 배열을 반환하지 원본 배열을 수정하지 않음


배열 변환하기 / 교체하기

map 쓰셈

 

새 배열을 반환하지 원본 배열을 수정하지 않음


배열에 항목 삽입하기

slice 쓰셈

setArtists([
	...artists.slice(0, insertAt),
    { id: nextId++, name: name },
    ...artists.slice(insertAt)
])

배열에 기타 변경 적용하기

새 배열을 반환하는게 아닌 원본 바꾸는 메소드의 경우엔 스프레드 연산자로 배열을 복사해서 사용하자

 

단, 이는 얕은 복사이기 때문에 배열 내부 객체에 접근하지말자.

 

const nextList = [...list];
nextList[0].seen = true; // 문제 : list[0]을 변경시킴 ;;
setList(nextList);

배열 내부의 객체 업데이트하기

배열 내부의 객체는 주소값임

 

그래서 중첩된 state를 업데이트할 때, 업데이트하려는 지점부터 최상위 레벨까지의 복사본을 만들어야 한다.

 

이거는 링크에서 예제 보면서 얘기해보자

 

 

배열 State 업데이트하기 – React

The library for web and native user interfaces

ko.react.dev


Immer로 간결한 업데이트 로직 작성하기

이거도 링크에서 예제 보면서 얘기하자

 

 

배열 State 업데이트하기 – React

The library for web and native user interfaces

ko.react.dev

 

참 좋네


챌린지

 

배열 State 업데이트하기 – React

The library for web and native user interfaces

ko.react.dev

 

1번 (map 사용)

function handleIncreaseClick(productId) {
    setProducts(products.map(product => {
      if (product.id === productId) {
        return {...product, count: product.count + 1};
      }

      return product;
    }))
  }

 

2번 (map 사용)

  function handleDecreaseClick(productId) {
    setProducts(products.map(product => {
      if (product.id === productId) {
        return {
          ...product,
          count: product.count - 1
        };
      } else {
        return product;
      }
    }))
  }

 

3번, 아쒸 문제 진짜 많네

// TODO 추가
function handleAddTodo(title) {
    setTodos([...todos, {
      id: nextId++,
      title: title,
      done: false
    }]);
  }
  
  
// TODO 제거
  function handleDeleteTodo(todoId) {
    setTodos(todos.filter(todo => todo.id === todoId));
  }
  
// TODO 수정
 function handleChangeTodo(nextTodo) {
    setTodos(todos.map(todo => {
      if (todo.id === nextTodo.id) {
        return nextTodo;
      }

      return todo;
    }))
  }

 

4번 (근데 써보니 Immer 쓰기 싫어짐 ;; 뭔가 원본 바꾸는 개념 자체를 가지기 싫은데, 계속 생각나는게 인지부조화가 들어서 싫음)

import { useState } from 'react';
import { useImmer } from 'use-immer';
import AddTodo from './AddTodo.js';
import TaskList from './TaskList.js';

let nextId = 3;
const initialTodos = [
  { id: 0, title: 'Buy milk', done: true },
  { id: 1, title: 'Eat tacos', done: false },
  { id: 2, title: 'Brew tea', done: false },
];

export default function TaskApp() {
  const [todos, setTodos] = useImmer(
    initialTodos
  );

  function handleAddTodo(title) {
    // 계속 이래서 틀림 ;;
    // setTodos(draft => draft.push({id: nextId++, title: title, done: false});
    setTodos(draft => {draft.push({
      id: nextId++,
      title: title,
      done: false
    })});
  }

function handleChangeTodo(nextTodo) {
    setTodos(draft => {
      const todo = draft.find(t =>
        t.id === nextTodo.id
      );
      todo.title = nextTodo.title;
      todo.done = nextTodo.done;
    })
  }

  function handleDeleteTodo(todoId) {
    setTodos(draft => {
      const index = draft.findIndex(t => t.id === todoId);

      draft.splice(index, 1);
    })
  }
  return (
    <>
      <AddTodo
        onAddTodo={handleAddTodo}
      />
      <TaskList
        todos={todos}
        onChangeTodo={handleChangeTodo}
        onDeleteTodo={handleDeleteTodo}
      />
    </>
  );
}

 

Immer 쓰지말자 ;

반응형
LIST