본문 바로가기
Research/React

React input 추가/삭제 기능 + 컴포넌트 반복 표현

by RIEM 2023. 10. 24.
const IterationSample = () => {
  const names = ['Jin', 'Paul', 'Law', 'Yosimitsu'];
  const nameList = names.map((name, index) => <li key={index}>{name}</li>);
  return <ul>{nameList}</ul>;
};

export default IterationSample;

This is the code to display list through iteration. Key is required so index from map function is passed. But using index is not perfect way. You should only use this way when there is unique value. Using index as key is not efficient way when it is re-rendering.

input으로 데이터를 넣을 수 있는 기능을 구현하면 이렇다.

import { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: 'Jin Kazama' },
    { id: 2, text: 'Paul pheonix' },
    { id: 3, text: 'Kazuya' },
    { id: 4, text: 'Heihachi' },
  ]);

  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChange = (e) => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,
      text: inputText,
    });
    setNextId(nextId + 1);
    setNames(nextNames);
    setInputText('');
  };

  const namesList = names.map((name) => <li key={name.id}>{name.text}</li>);
  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onClick={onClick}>Add..</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

 

놀랍다. push 함수 대신 concat을 사용하는 이유는 원본을 수정하지 않고 새 복사본을 만들기 위함이다. 기존 상태를 그대로 유지한 채 새로운 값을 상태로 설정하는 것을 '불변성 유지'라 한다. 리액트에서는 불변성 유지가 컴포넌트 성능 최적화에 유용하다 한다.

지우기 기능 추가하기

import { useState } from 'react';

const IterationSample = () => {
  const [names, setNames] = useState([
    { id: 1, text: 'Jin Kazama' },
    { id: 2, text: 'Paul pheonix' },
    { id: 3, text: 'Kazuya' },
    { id: 4, text: 'Heihachi' },
  ]);

  const [inputText, setInputText] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChange = (e) => setInputText(e.target.value);
  const onClick = () => {
    const nextNames = names.concat({
      id: nextId,
      text: inputText,
    });
    setNextId(nextId + 1);
    setNames(nextNames);
    setInputText('');
  };

  const onRemove = (id) => {
    const nextNames = names.filter((name) => name.id !== id);
    setNames(nextNames);
  };

  const namesList = names.map((name) => (
    <li key={name.id} onDoubleClick={() => onRemove(name.id)}>
      {name.text}
    </li>
  ));

  return (
    <>
      <input value={inputText} onChange={onChange} />
      <button onClick={onClick}>Add..</button>
      <ul>{namesList}</ul>
    </>
  );
};

export default IterationSample;

key값은 중복되면 안되고 유니크 해야한다. 중복되면 에러 발생한다고 한다.

컴포넌트 최적화에 도움이 되는 불변성 유지를 위해 데이터 추가 시 concat을 쓰고 제거 시 filter을 쓴다. 

 

Franklin note 적용하기

이를 Franklin-note를 위해 수정해보았다. 줄여서 FN라 하겠다. FN은 문장, 문장의 저자, 출처 url, 총 3개의 데이터를 기입할 수 있는 input 창이 있다. 데이터를 기입하고 button을 누르면 3개의 데이터를 가진 하나의 세트를 컴포넌트로 변환한 뒤, input창을 초기화하는 작업이 필요하다. 이를 구현하면 이렇다.

import { useState } from 'react';

const IterationSample = () => {
  const [quotes, setQuotes] = useState([
    {
      id: 1,
      quote:
        'The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart',
      author: 'Hellen Keller',
      url: 'https://www.brainyquote.com/quotes/helen_keller_101301',
    },

    {
      id: 2,
      quote: 'The purpose of our lives is to be happy',
      author: 'Dalai Lama',
      url: 'https://parade.com/937586/parade/life-quotes/',
    },
    {
      id: 3,
      quote: 'Get busy living or get busy dying.',
      author: 'Stephen King',
      url: 'https://parade.com/937586/parade/life-quotes/',
    },
    {
      id: 4,
      quote: 'You only live once, but if you do it right, once is enough',
      author: 'mae west',
      url: 'https://parade.com/937586/parade/life-quotes/',
    },
  ]);

  const [inputSentence, setInputSentence] = useState('');
  const [inputAuthor, setInputAuthor] = useState('');
  const [inputUrl, setInputUrl] = useState('');
  const [nextId, setNextId] = useState(5);

  const onChangeSentence = (e) => setInputSentence(e.target.value);
  const onChangeAuthor = (e) => setInputAuthor(e.target.value);
  const onChangeUrl = (e) => setInputUrl(e.target.value);

  const onClick = () => {
    const nextQuotes = quotes.concat({
      id: nextId,
      quote: inputSentence,
      author: inputAuthor,
      url: inputUrl,
    });
    setNextId(nextId + 1);
    setQuotes(nextQuotes);
    setInputSentence('');
    setInputAuthor('');
    setInputUrl('');
  };

  const onRemove = (id) => {
    const nextQuotes = quotes.filter((quote) => quote.id !== id);
    setQuotes(nextQuotes);
  };

  const quotesList = quotes.map((quote) => (
    <li key={quote.id} onDoubleClick={() => onRemove(quote.id)}>
      "{quote.quote}" by {quote.author} from {quote.url}
    </li>
  ));

  return (
    <>
      <input
        value={inputSentence}
        onChange={onChangeSentence}
        placeholder="sentence"
      />
      <input
        value={inputAuthor}
        onChange={onChangeAuthor}
        placeholder="author"
      />
      <input value={inputUrl} onChange={onChangeUrl} placeholder="url" />

      <button onClick={onClick}>Add</button>
      <ul>{quotesList}</ul>
    </>
  );
};

export default IterationSample;

 

각 input창에 데이터를 기입하고 Add 버튼을 눌러보겠다.

Yes. 잘 작동 된다!

댓글