본문 바로가기

React.js

React(리액트): 배열 항목 추가, 제거, 수정

반응형

배열에 항목 추가하기

이번에는 배열에 항목을 추가하는 방법에 대해 알아보자.

 

이 예제에서는 CreateUser 컴포넌트에서는 별 다른 상태 관리를 하지 않고 화면 구성만 할 뿐 상태 관리는

App 컴포넌트에서 해준다.

 

App.js

import "./App.css";
import UserList from './UserList';
import {useRef, useState} from 'react';
import CreateUser from './CreateUser';

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: '',
  })
  const {username, email} = inputs;
  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    })
  }
  const [users, setUsers] = useState([
    {
        id:1,
        username: 'paboke22',
        email: 'paboke22@gmail.com'
    },
    {
        id:2,
        username: 'tester',
        email: 'tester@example.com'
    },
    {
        id:3,
        username: 'guest',
        email: 'guest@example.com'
    }
  ]);

  const nextId = useRef(4);

  const onCreate = () =>{
    const user = {
      id: nextId.current,
      username,
      email,
    }
    setUsers([...users, user])
  setInputs({
    username: '',
    email: ''
  })
  console.log(nextId.current);
  nextId.current += 1;
}

  return (
  <>
    <CreateUser 
      username={username} 
      email={email} 
      onChange={onChange} 
      onCreate={onCreate}
    />
    <UserList users={users}/>
  </>
  );
}

export default App;

CreateUser.js

import React from 'react';

function CreateUser({username, email, onChange, onCreate}){
    return (
        <div>
            <input 
                name="username" 
                placeholder="계정명" 
                onChange={onChange} 
                value={username}
            />
            <input 
                name="email" 
                placeholder="이메일" 
                onChange={onChange} 
                value={email}
            />
            <button onClick={onCreate}>등록</button>
        </div>
    )
}

export default CreateUser;

전체 코드는 다음과 같다. 아래에서 코드 하나하나 살펴보자.

 

const [inputs, setInputs] = useState({
    username: '',
    email: '',
  })

여러 개의 input 관리 값을 관리할 때는 useState를 사용하는데 input 값이 두 개라고 해서 useState두 개를 사용하는

것이 아니라, 객체 형태로 상태를 만들어주면 된다.

inputs라는 상태를 만들어주었고 이 상태에서 사용할 username과 email을 공백으로 설정해주었다.

 

const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    })
  }

onChange는 이벤트 e를 가지고 오고 e.target.name과 e.target.value를 비구조화 할당 문법을 사용해 추출해준다.

setInputs({})에는 spread문법인... inputs를 사용해 기존의 내용을 가져오고 새로 추가된 name과 value를 덮어씌워 준다.

여기서 name은 CreateUser 컴포넌트의 unsername 또는 email이고 value는 input에 입력한 값이 된다.

 

const [users, setUsers] = useState([
    {
        id: 1,
        username: 'paboke22',
        email: 'paboke22@gmail.com'
    },
    {
        id:2,
        username: 'tester',
        email: 'tester@example.com'
    },
    {
        id: 3,
        username: 'guest',
        email: 'guest@example.com'
    }
  ]);

기존의 users 배열은 컴포넌트의 상태로써 관리되고 있지 않아 컴포넌트의 상태로써 관리하기 위해 다음과 같이 

바꾸었다. 단순히 useState로 감싸주었다. 이렇게 함으로써 users 배열이 업데이트될 경우 렌더링이 되도록 만들 수 

있다. 

 

배열에서도 users에 어떤 값을 추가한다고 해서 'user.push' 이런 식으로 하면 안 된다. 그 대신 기존의 배열을 바꾸지 

않으면서 새로운 배열을 만들어 새로운 배열에 변화를 주는 방식으로 해야 한다. 참고로 push, splice, sort와 같은 함수를 사용할 수는 없다.

배열의 불변성을 지키면서 새로운 방법을 추가하는 방법에는 두 가지가 있다. 첫 번째는 '... inputs'와 같이 스프레드 함수를 사용하는 것이고 두 번째는 'concat'을 사용하는 것이다. 

 

const onCreate = () =>{
    const user = {
      id: nextId.current,
      username,
      email,
    }
    setUsers([...users, user]) // = (users.concat(user));
  setInputs({
    username: '',
    email: ''
  })

onCreate 함수에서 새로운 'user' 객체를 만들어준다. user 객체의 id는 1씩 증가하는 nextId.current이고 username은 

현재 inputs가 가리키고 있는 username에 해당한다. email도 마찬가지로 inputs가 가리키는 email에 해당한다.

setUsers([... users, user ])에서는 기존의 배열을 복사해서 넣고 새로운 배열을 만들어 기존 배열에 붙여준다. 기존 배열은 복사해서 넣어주면서 새 항목을 추가해주는 방법이다. concat을 사용할 수도 있다.

 

다음과 같이 배열에 새 항목을 추가할 수 있다.

 

배열에 항목 제거하기

App.js

import "./App.css";
import UserList from './UserList';
import {useRef, useState} from 'react';
import CreateUser from './CreateUser';

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: '',
  })
  const {username, email} = inputs;
  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    })
  }
  const [users, setUsers] = useState([
    {
        id: 1,
        username: 'paboke22',
        email: 'paboke22@gmail.com'
    },
    {
        id:2,
        username: 'tester',
        email: 'tester@example.com'
    },
    {
        id: 3,
        username: 'guest',
        email: 'guest@example.com'
    }
  ]);

  const nextId = useRef(4);

  const onCreate = () =>{
    const user = {
      id: nextId.current,
      username,
      email,
    }
    setUsers([...users, user])
  setInputs({
    username: '',
    email: ''
  })
  console.log(nextId.current);
  nextId.current += 1;
}

const onRemove = id => {
  setUsers(users.filter(user => user.id !== id));
}

  return (
  <>
    <CreateUser 
      username={username} 
      email={email} 
      onChange={onChange} 
      onCreate={onCreate}
    />
    <UserList users={users} onRemove={onRemove}/>
  </>
  );
}

export default App;

UserList.js

import React, { useState } from 'react'

function User({user, onRemove}){
    const { username, email, id} = user;
    return (
        <div>
              <b>{username}</b><span>({email})</span>
              <button onClick={() => onRemove(id)}>삭제</button>
          </div>

    )
}

function UserList({users, onRemove}) {
    return (
        <div>
          {
              users.map(
                  user => (<User user={user} key={user.id} onRemove={onRemove}/>)
              )
          }
        </div>
    )
}

export default UserList;

삭제 버튼이 추가 되었고 등록한 내용 삭제가 가능하다. 

return (
        <div>
              <b>{username}</b><span>({email})</span>
              <button onClick={() => onRemove(id)}>삭제</button>
          </div>

    )

UserList.js의 User 컴포넌트에서 추가된 부분을 살펴보자. onClick에서 새로운 함수를 만들어준 부분이 헷갈릴 수 있는데,

이게 어떤 의미냐면 '이 버튼을 눌렀을 땐 '()' 함수를 호출한다. 이 함수에서는 props로 받아온 onRemove에 id 값을 

파라미터로 넣어서 호출해줄 것이다.'는 뜻이다.  결국 특정 id에 해당하는 값을 삭제하겠다는 의미이다.

 

만약 여기서 onClick={onRemove(id)}처럼 작성한다면 컴포넌트가 렌더링 될 때 onRemove가 호출되면서 렌더링 되는 

시점에 삭제가 되어 버린다. 반드시 onClick={() => onRemove(id)} 이렇게 작성해야 한다.

 

우리가 배열에서 특정 아이템을 삭제할 땐 불변성을 지키면서 업데이트를 해주어야 하는데, 이때 'filter'라는 함수를

사용하면 아주 편리하다. filter 함수는 배열에서 특정 조건을 만족하는 원소들만 추출해서 새로운 배열을 만들어준다.

 

const onRemove = id => {
  setUsers(users.filter(user => user.id !== id));
}

다음 코드는 users 배열에다가 필터를 걸어서 각 user 객체들을 확인하는데 그중에서 user.id가 파라미터로 가져온 것이랑 일치하지 않는 것들만 추출하겠다는 뜻이다. 만약 파라미터로 가져온 id와 user.id가 일치한다면 조건은 false가 되어 

해당 배열에서 제외될 것이다. 

 

onRemove 함수를 App.js에서 선언하고 UserList.js로 넘겨준다. UserList.js의 UserList 컴포넌트는 User 컴포넌트에게 

다시 onRemove를 넘겨주고 최종적으로 User 컴포넌트에서 삭제 버튼을 누르면 onRemove 함수를 호출해 삭제를

수행한다. 

 

반응형

 

배열에 항목 수정하기

이번엔 배열 안에 들어있는 특정 항목들을 수정하는 방법에 대해서 알아보자.

구현할 기능은 우리가 계정명을 클릭하면 계정명이 초록색으로 바뀌고 다시 누르면 검은색으로 바뀌는 기능이다.

 

App.js

import "./App.css";
import UserList from './UserList';
import {useRef, useState} from 'react';
import CreateUser from './CreateUser';

function App() {
  const [inputs, setInputs] = useState({
    username: '',
    email: '',
  })
  const {username, email} = inputs;
  const onChange = e => {
    const {name, value} = e.target;
    setInputs({
      ...inputs,
      [name]: value
    })
  }
  const [users, setUsers] = useState([
    {
        id: 1,
        username: 'paboke22',
        email: 'paboke22@gmail.com',
        active: true
    },
    {
        id:2,
        username: 'tester',
        email: 'tester@example.com',
        active: false,
    },
    {
        id: 3,
        username: 'guest',
        email: 'guest@example.com',
        active: false,
    }
  ]);

  const nextId = useRef(4);

  const onCreate = () =>{
    const user = {
      id: nextId.current,
      username,
      email,
    }
    setUsers([...users, user])
  setInputs({
    username: '',
    email: ''
  })
  console.log(nextId.current);
  nextId.current += 1;
}

const onRemove = id => {
  setUsers(users.filter(user => user.id !== id));
}

const onToggle = id => {
  setUsers(users.map(
    user => user.id === id
    ? {...user, active: !user.active}
    : user
  ))
}

  return (
  <>
    <CreateUser 
      username={username} 
      email={email} 
      onChange={onChange} 
      onCreate={onCreate}
    />
    <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
  </>
  );
}

export default App;

UserList.js

import React from 'react'

function User({user, onRemove, onToggle}){
    const { username, email, id, active} = user;
    return (
        <div>
            <b 
            style={{
                  color: active ? 'green':'black',
                  cursor: 'pointer'
              }}
              onClick={() => onToggle(id)}
            >
                  {username}
            </b>
            &nbsp;
            <span>({email})</span>
              <button onClick={() => onRemove(id)}>삭제</button>
          </div>

    )
}

function UserList({users, onRemove, onToggle}) {
    return (
        <div>
          {
              users.map(
                  (user) => (
                  <User 
                    user={user} 
                    key={user.id} 
                    onRemove={onRemove} 
                    onToggle={onToggle}
                    />
                )
              )
          }
        </div>
    );
}

export default UserList;

클릭하면 글 색깔을 바꾸는 기능을 구현했다.

const [users, setUsers] = useState([
    {
        id: 1,
        username: 'paboke22',
        email: 'paboke22@gmail.com',
        active: true
    },
    {
        id:2,
        username: 'tester',
        email: 'tester@example.com',
        active: false,
    },
    {
        id: 3,
        username: 'guest',
        email: 'guest@example.com',
        active: false,
    }
  ]);

먼저 App.js의 users 배열에 active라는 값을 추가했다. 이 값에 따라 글 색깔이 변화하게 만들 것이다.

 

<b 
            style={{
                  color: active ? 'green':'black',
                  cursor: 'pointer'
              }}

UserList.js의 User 컴포넌트에서 user값을 받아오고 user 값에 따라 color가 바뀌도록 style을 선언해주었다.

추가적으로 cursor라는 값을 'pointer'로 설정해서 마우스를 갖다 대면 손가락 모양으로 바뀌게 해 주었다.

 

const onToggle = id => {
  setUsers(users.map(
    user => user.id === id
    ? {...user, active: !user.active}
    : user
  ))
}

App.js에서 onToggle 함수를 구현했다. id 값을 파라미터로 받아오고 users에 있는 특정 id를 가지고 선택해서 active 값을 반전시켜 줄 것이다. 불변성을 지키면서 배열을 업데이트할 때에도 'map'을 사용할 수 있다. 기존에 map함수는 특정

배열을 새로운 형태로 변환시켜줄 때 사용했었는데, 배열에 있는 특정 아이템만 업데이트할 때에도 map함수를 

사용할 수 있다. 

전체 배열을 업데이트하는 것처럼 하지만 만약 id가 일치하면 업데이트하고 일치하지 않으면 기존의 것을 그대로 두는 방식으로 업데이트를 한다. 4번 줄의 '... user'를 사용해 기존의 배열을 가져와 불변성을 유지시켜 준다.

 

onToggle도 onRemove와 마찬가지로 App.js에서 구현하고 UserList에서 이를 받아 사용한다.

 

배열 안에 있는 원소를 업데이트할 때에는 map함수를 사용해서 구현할 수 있다는 것과 특정 객체를 업데이트 할 때는 

4번 줄과 같이 '... user'를 해줘서 기존의 user를 수정하는 것이 아니라 새로운 객체를 만들어 기존의 user가 갖고 있던 값들을 넣어주고 특정 값을 덮어쓰는 형태로 구현을 해주어야 한다.

 

지금은 불변성을 유지하는 것이 익숙하지 않을 수 있다. 익숙해질 필요가 있다.

나중에는 immer.js라는 불변성을 조금 더 쉽게 유지해주는 라이브러리도 사용해 볼 것이다.

 

 

우리가 배열 안에 있는 값을 추가할 때는 'spread' 연산자를 사용하거나 'concat'함수를 사용해 주어야 하고, 제거

할 때는 'filter'함수를, 특정값만 업데이트할 때에는 'map'함수를 사용하면 되겠다.

 

 

반응형