흰 스타렉스에서 내가 내리지

변이를 사용하지 않고 배열 업데이트하기 본문

React

변이를 사용하지 않고 배열 업데이트하기

주씨. 2024. 5. 19. 15:31
728x90

배열에 추가하기

push()는 배열을 변이시키므로 사용하면 안됩니다:

 

대신, 기존 아이템과 새 아이템을 포함한 새 배열을 생성합니다. 이를 위해 여러 가지 방법이 있지만, 가장 간단한 방법은 ... 배열 스프레드 구문을 사용하는 것입니다

 

import { useState } from 'react';

let nextId = 0;

export default function List() {
  const [name, setName] = useState('');
  const [artists, setArtists] = useState([]);

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={() => {
        setArtists([
          ...artists,
          { id: nextId++, name: name }
        ]);
      }}>Add</button>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>{artist.name}</li>
        ))}
      </ul>
    </>
  );
}

 

 

 

배열에서 제거하기

배열에서 아이템을 제거하는 가장 쉬운 방법은 해당 아이템을 필터링하여 제외하는 것입니다.

즉, 해당 아이템을 포함하지 않은 새 배열을 생성합니다. 이를 위해 filter 메서드를 사용할 수 있습니다. 

 

import { useState } from 'react';

let initialArtists = [
  { id: 0, name: 'Marta Colvin Andrade' },
  { id: 1, name: 'Lamidi Olonade Fakeye'},
  { id: 2, name: 'Louise Nevelson'},
];

export default function List() {
  const [artists, setArtists] = useState(
    initialArtists
  );

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>
            {artist.name}{' '}
            <button onClick={() => {
              setArtists(
                artists.filter(a =>
                  a.id !== artist.id
                )
              );
            }}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </>
  );
}

 

 

 

배열 변형하기

배열의 일부 또는 모든 아이템을 변경하고자 할 때는 map()을 사용하여 새로운 배열을 생성할 수 있습니다.

map에 전달하는 함수는 데이터나 인덱스(또는 둘 모두)를 기반으로 각 아이템에 대해 수행할 작업을 결정할 수 있습니다.

 

import { useState } from 'react';

let initialShapes = [
  { id: 0, type: 'circle', x: 50, y: 100 },
  { id: 1, type: 'square', x: 150, y: 100 },
  { id: 2, type: 'circle', x: 250, y: 100 },
];

export default function ShapeEditor() {
  const [shapes, setShapes] = useState(
    initialShapes
  );

  function handleClick() {
    const nextShapes = shapes.map(shape => {
      if (shape.type === 'square') {
        // No change
        return shape;
      } else {
        // Return a new circle 50px below
        return {
          ...shape,
          y: shape.y + 50,
        };
      }
    });
    // Re-render with the new array
    setShapes(nextShapes);
  }

  return (
    <>
      <button onClick={handleClick}>
        Move circles down!
      </button>
      {shapes.map(shape => (
        <div
          key={shape.id}
          style={{
          background: 'purple',
          position: 'absolute',
          left: shape.x,
          top: shape.y,
          borderRadius:
            shape.type === 'circle'
              ? '50%' : '',
          width: 20,
          height: 20,
        }} />
      ))}
    </>
  );
}

 

 

배열에서 아이템 교체하기

일부 또는 모든 아이템을 교체하고자 하는 경우, 배열을 변이시키는 할당(arr[0] = 'bird')과 같은 방식은 사용해서는 안 됩니다.

대신 map을 사용하여 교체할 수 있습니다.

 

import { useState } from 'react';

let initialCounters = [
  0, 0, 0
];

export default function CounterList() {
  const [counters, setCounters] = useState(
    initialCounters
  );

  function handleIncrementClick(index) {
    const nextCounters = counters.map((c, i) => {
      if (i === index) {
        // Increment the clicked counter
        return c + 1;
      } else {
        // The rest haven't changed
        return c;
      }
    });
    setCounters(nextCounters);
  }

  return (
    <ul>
      {counters.map((counter, i) => (
        <li key={i}>
          {counter}
          <button onClick={() => {
            handleIncrementClick(i);
          }}>+1</button>
        </li>
      ))}
    </ul>
  );
}

 

 

배열에 삽입하기

때로는 시작이나 끝이 아닌 특정 위치에 아이템을 삽입하고 싶을 수 있습니다.

이를 위해 ... 배열 스프레드 구문과 slice() 메서드를 함께 사용할 수 있습니다.

slice() 메서드는 배열을 "슬라이스"하여 자를 수 있습니다.

아이템을 삽입하기 위해, 삽입 지점 이전의 슬라이스, 그리고 새로운 아이템, 그리고 원래 배열의 나머지를 포함하는 배열을 생성합니다.

 

import { useState } from 'react';

let nextId = 3;
const initialArtists = [
  { id: 0, name: 'Marta Colvin Andrade' },
  { id: 1, name: 'Lamidi Olonade Fakeye'},
  { id: 2, name: 'Louise Nevelson'},
];

export default function List() {
  const [name, setName] = useState('');
  const [artists, setArtists] = useState(
    initialArtists
  );

  function handleClick() {
    const insertAt = 1; // Could be any index
    const nextArtists = [
      // Items before the insertion point:
      ...artists.slice(0, insertAt),
      // New item:
      { id: nextId++, name: name },
      // Items after the insertion point:
      ...artists.slice(insertAt)
    ];
    setArtists(nextArtists);
    setName('');
  }

  return (
    <>
      <h1>Inspiring sculptors:</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
      />
      <button onClick={handleClick}>
        Insert
      </button>
      <ul>
        {artists.map(artist => (
          <li key={artist.id}>{artist.name}</li>
        ))}
      </ul>
    </>
  );
}

 

 

 

Immer를 사용하여 간결한 업데이트 로직 작성하기

import { useState } from 'react';
import { useImmer } from 'use-immer';

let nextId = 3;
const initialList = [
  { id: 0, title: 'Big Bellies', seen: false },
  { id: 1, title: 'Lunar Landscape', seen: false },
  { id: 2, title: 'Terracotta Army', seen: true },
];

export default function BucketList() {
  const [myList, updateMyList] = useImmer(
    initialList
  );
  const [yourList, updateYourList] = useImmer(
    initialList
  );

  function handleToggleMyList(id, nextSeen) {
    updateMyList(draft => {
      const artwork = draft.find(a =>
        a.id === id
      );
      artwork.seen = nextSeen;
    });
  }

  function handleToggleYourList(artworkId, nextSeen) {
    updateYourList(draft => {
      const artwork = draft.find(a =>
        a.id === artworkId
      );
      artwork.seen = nextSeen;
    });
  }

  return (
    <>
      <h1>Art Bucket List</h1>
      <h2>My list of art to see:</h2>
      <ItemList
        artworks={myList}
        onToggle={handleToggleMyList} />
      <h2>Your list of art to see:</h2>
      <ItemList
        artworks={yourList}
        onToggle={handleToggleYourList} />
    </>
  );
}

function ItemList({ artworks, onToggle }) {
  return (
    <ul>
      {artworks.map(artwork => (
        <li key={artwork.id}>
          <label>
            <input
              type="checkbox"
              checked={artwork.seen}
              onChange={e => {
                onToggle(
                  artwork.id,
                  e.target.checked
                );
              }}
            />
            {artwork.title}
          </label>
        </li>
      ))}
    </ul>
  );
}

 

 

이는 원래 상태를 변이시키지 않고, Immer가 제공하는 특수 draft 객체를 변이합니다. 마찬가지로 draft의 내용에 push()나 pop()과 같은 변이 메서드를 적용할 수도 있습니다.

 

내부적으로 Immer는 draft에 대한 변경 사항에 따라 항상 다음 상태를 새롭게 구성합니다. 이렇게 하면 상태 변이 없이 이벤트 핸들러를 매우 간결하게 유지할 수 있습니다.