[React] 배열 +

sue·2021년 1월 18일
0

react note

목록 보기
7/17

배열에 항목 추가

CreateUser.js

import React from "react";

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

export default CreateUser;

상태 관리를 부모 컴포넌트인 App에서 하게 하고, input의 값 및 이벤트로 등록할 함수들을 props로 넘겨받아서 사용한다.

App.js

import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";

function App() {
  const [inputs, setInputs] = useState({
    username: "",
    email: "",
    // 초기값 공백 설정
  });

  const { username, email } = inputs; // 미리 추출
  const onChange = (e) => {
    const { name, value } = e.target;
    // name과 value를 e.target에서 가져오도록!!
    setInputs({
      ...inputs, // 기존 배열 복사!!!
      [name]: value,
      // 받아온 name값을 value로 덮어 씌운다! 
      // (name이 가리키고 있는 게 username이면 username을 바꾸고,
      // email이면 email을 바꾼다)
    });
  };

  const [users, setUsers] = useState([
    {
      id: 1,
      username: "su",
      email: "susu@gmail.com",
    },
    {
      id: 2,
      username: "liz",
      email: "lili@gmail.com",
    },
    {
      id: 3,
      username: "ro",
      email: "ro@gmail.com",
    },
  ]);

  const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용

  const onCreate = () => {
    // 버튼이 클릭될 때 input에 있는 값을 지우도록
    setInputs({
      username: "",
      email: "",
    });
    console.log(nextId.current); //4
    nextId.current += 1;
  };

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

export default App;

배열을 변화시킬 때도 객체와 마찬가지로 불변성을 지켜주어야 하기 때문에, 배열의 push, splice, sort 같은 함수를 사용하면 안된다. 만약 꼭 사용해야 하는 경우에는, 기존의 배열을 한번 복사한 후에 사용해야 한다.

불변성을 지키며 새 항목 추가하는 방법

1) spread 연산자 사용

import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";

function App() {
  const [inputs, setInputs] = useState({
    username: "",
    email: "",
  });

  const { username, email } = inputs; // 미리 추출
  const onChange = (e) => {
    const { name, value } = e.target;
    // 이 부분 까먹지 말기!! name과 value를 e.target에서 가져오도록!!
    setInputs({
      ...inputs, // 이 부분도!! 기존 배열 복사!!!
      [name]: value,
      // 받아온 name값을 value로 덮어 씌운다! (name이 가리키고 있는 게 username이면 username을 바꾸고, email이면 email을 바꾼다)
    });
  };

  const [users, setUsers] = useState([
    {
      id: 1,
      username: "su",
      email: "susu@gmail.com",
    },
    {
      id: 2,
      username: "liz",
      email: "lili@gmail.com",
    },
    {
      id: 3,
      username: "ro",
      email: "ro@gmail.com",
    },
  ]);

  const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용

  const onCreate = () => {
    // 버튼이 클릭될 때 input에 있는 값을 지우도록
    const user = {
      // 새로운 유저 객체 생성
      id: nextId.current,
      username,
      email,
    };
    setUsers([...users, user]);
    // 기존배열복사 후, 새로운 배열 만듬
    setInputs({
      username: "",
      email: "",
    });

    nextId.current += 1;
  };

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

export default App;

2) concat 함수 사용

contcat함수는 기존 배열을 수정하는 것이 아니라 여러 개의 배열을 하나의 배열로 합쳐준다.

  const onCreate = () => {
    // 버튼이 클릭될 때 input에 있는 값을 지우도록
    const user = {
      // 새로운 유저 객체 생성
      id: nextId.current,
      username,
      email,
    };
    // setUsers([...users, user]);
    setUsers(users.concat(user));
    setInputs({
      username: "",
      email: "",
    });

    nextId.current += 1;
  };

배열에 항목 삭제

import React from "react";

function User({ user, onRemove }) {
  const { username, email, id } = user;
  return (
    <div>
      <b>{username}</b> <span>{email}</span>
      <button onClick={() => onRemove(id)}>삭제</button>
      {/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데,
      id가 특정값인 onRemove 함수 실행하는 함수 */}
    </div>
  );
}

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

삭제 버튼이 클릭될 때, user.id값을 props로 받아올 onRemove함수의 파라미터로 넣어서 호출해줘야 한다.

불변성을 지키면서 배열에서 특정 원소를 제거하기 위해서는 filter 배열 내장 함수를 사용하는 것이 편리하다. 이 함수를 사용하면 배열에서 특정 조건을 만족하는 원소들만 추출하여 새로운 배열을 만들 수 있다.

import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";

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: "su",
      email: "susu@gmail.com",
    },
    {
      id: 2,
      username: "liz",
      email: "lili@gmail.com",
    },
    {
      id: 3,
      username: "ro",
      email: "ro@gmail.com",
    },
  ]);

  const nextId = useRef(4); 

  const onCreate = () => {
    // 버튼이 클릭될 때 input에 있는 값을 지우도록
    const user = {
      // 새로운 유저 객체 생성
      id: nextId.current,
      username,
      email,
    };
    // 기존배열복사 후, 새로운 배열
    setUsers(users.concat(user));
    setInputs({
      username: "",
      email: "",
    });

    nextId.current += 1;
  };

  const onRemove = (id) => {
    setUsers(users.filter((user) => user.id !== id));
    // users 배열에 filter를 걸어주고, 
    // 각 user 객체를 확인하는데 
    // 그중 user.id가 파라미터로 가져온 id와 일치하지 않는 원소만 추출해서 새로운 배열을 만듬 
    // 파라미터가 일치하면, 값이 false가 되면서 해당 배열에서 제외
  };
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} />
    </>
  );
}

export default App;

onClick 에서 아래와 같이 onRemove() 함수를 따로 호출하는 함수를 새로 만들어주지 않으면, 렌더링 되는 순간 onRemove함수가 바로 실행되어 버린다. 따라서 반드시 함수를 따로 만들어 넣어주어야 한다.

<button onClick={onRemove(id)}>삭제</button>

X

<button onClick={() => onRemove(id)}>삭제</button>

배열에 항목 수정

계정명을 클릭했을 때 초록색으로 바뀌고 다시 누르면 검정색으로 바뀌도록 구현한다.

(1) 배열의 객체 안에 active 속성 추가

 const [users, setUsers] = useState([
    {
      id: 1,
      username: "su",
      email: "susu@gmail.com",
      active: true,
    },
    {
      id: 2,
      username: "liz",
      email: "lili@gmail.com",
      active: false,
    },
    {
      id: 3,
      username: "ro",
      email: "ro@gmail.com",
      active: false,
    },
  ]);

(2) User컴포넌트에서 active값에 따라 폰트 색상이 바뀌도록, 커서 포인터로 변경

function User({ user, onRemove, onToggle }) {
  const { username, email, id, active } = user;
  return (
    <div>
      <b
        style={{
          color: active ? "green" : "black",
          cursor: "pointer",
        }}
      >
        {username}
      </b>
      &nbsp;
      <span>{email}</span>
      <button onClick={() => onRemove(id)}>삭제</button>
      {/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데, id가 특정값인 onRemove 함수 실행하는 함수 */}
    </div>
  );
}

(3) App.js에서 onToggle함수 구현

배열 안에 원소를 업데이트할 때에도 map함수를 사용할 수 있다.
유저 객체를 확인하는데, 각 유저 객체의 아이디가 파라미터로 받은 아이디와 일치할 경우, 유저를 복사해서 기존 유저가 들고 있던 값을 넣어주고, 특정 값(active)만 업데이트한다. 일치하지 않을 경우 그대로 둔다.

import React, { useState, useRef } from "react";
import CreateUser from "./CreateUser";
import UserList from "./UserList";

function App() {
  const [inputs, setInputs] = useState({
    username: "",
    email: "",
  });

  const { username, email } = inputs; // 미리 추출
  const onChange = (e) => {
    const { name, value } = e.target;
    // 이 부분 까먹지 말기!! name과 value를 e.target에서 가져오도록!!
    setInputs({
      ...inputs, // 이 부분도!! 기존 배열 복사!!!
      [name]: value,
      // 받아온 name값을 value로 덮어 씌운다! (name이 가리키고 있는 게 username이면 username을 바꾸고, email이면 email을 바꾼다)
    });
  };

  const [users, setUsers] = useState([
    {
      id: 1,
      username: "su",
      email: "susu@gmail.com",
      active: true,
    },
    {
      id: 2,
      username: "liz",
      email: "lili@gmail.com",
      active: false,
    },
    {
      id: 3,
      username: "ro",
      email: "ro@gmail.com",
      active: false,
    },
  ]);

  const nextId = useRef(4); // 이 값이 바뀐다고 해서 굳이 컴포넌트가 리렌더링 될 필요가 없기 때문에 useRef 사용

  const onCreate = () => {
    // 버튼이 클릭될 때 input에 있는 값을 지우도록
    const user = {
      // 새로운 유저 객체 생성
      id: nextId.current,
      username,
      email,
    };
    // setUsers([...users, user]);
    // 기존배열복사 후, 새로운 배열
    setUsers(users.concat(user));
    setInputs({
      username: "",
      email: "",
    });

    nextId.current += 1;
  };

  const onRemove = (id) => {
    setUsers(users.filter((user) => user.id !== id));
    // users 배열에 filter를 걸어주고, 각 user 객체를 확인하는데 그중에서 user.id가 파라미터로 가져온 아이디와 일치하지 않는 것들만 추출
    // 그러면 파라미터가 일치하면, 값이 false가 되면서 해당 배열에서 제외된다.
  };

  const onToggle = (id) => {
    setUsers(
      user.map((user) =>
        user.id === id ? { ...user, active: !user.active } : user
      )
    );
  };
  // 배열안에 원소를 업데이트할 때에도 map함수를 사용
  // 유저 객체를 확인하는데, 각 유저 객체의 id가 파라미터로 받은 id와 일치할 경우, 
  // 유저를 복사해서 기존 유저가 들고 있던 값을 넣어주고, 특정 값(active)만 반전시킴 
  // 일치하지 않을 경우 그대로 
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} onRemove={onRemove} onToggle={onToggle} />
    </>
  );
}

export default App;

(4) onToggle 함수 받아와 User에게 전달, onToggleid 넣어서 호출

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>
      {/* 이 버튼이 눌렸을 때 새로운 함수를 만드는데, id가 특정값인 onRemove 함수 실행하는 함수 */}
    </div>
  );
}

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

0개의 댓글