[React] 배열 렌더링하기

jiyoung·2021년 9월 18일
0

배열 렌더링

보통 리액트를 통해 배열을 렌더링하면 기본적으로는 컴포넌트에 'users[0]' 이런식으로 해서 직접 넣어줘서 코드를 작성할 것이다. 하지만 이렇게 한다면 계속 JSX를 작성해야하기 때문에 굉장히 불편하고 번거로울 것이다.

따라서 배열 컴포넌트 외에 렌더링에 필요한 컴포넌트를 하나 더 생성한다. 그리고 동적인 데이터 처리를 위해 자바스크립트의 내장함수 map()을 사용하여 동적인 배열을 렌더링해준다. 마지막으로 배열을 렌더링 할 때는 key라는 props를 설정해야한다. key는 배열의 각 원소들이 가지고 있는 고유값으로 설정해줘야한다. key가 있어야만 배열이 업데이트 될 때 효율적으로 렌더링 될 수 있기 때문이다.

이름과 이메일이 들어있는 배열 원소 렌더링하기

UserList.js
import React from 'react';

function User({ user }) {
  return (
    <div>
      <b>{user.username}</b> <span>({user.email})</span>
    </div>
  );
}

function UserList() {
  const users = [
    {
      id: 1,
      username: 'jenny',
      email: 'jenny@gmail.com'
    },
    {
      id: 2,
      username: 'user1',
      email: 'user1@gmail.com'
    },
    {
      id: 3,
      username: 'user2',
      email: 'user2@gmail.com'
    }
  ];

  return (
    <div>
      {users.map(user => (
        <User user={user} key={user.id} />
      ))}
    </div>
  );
}

export default UserList;

function User({ user }) {
  return (
    <div>
      <b>{user.username}</b> <span>({user.email})</span>
    </div>
  );
}

✔렌더링에 사용할 컴포넌트를 User라는 이름으로 하나 더 생성해준다. <User /> 컴포넌트에 props로 준 배열의 각 원소인 {user}를 파라미터에 받아온다. (user는 각 배열의 원소를 나타내는 하나의 변수임) 각 배열 원소의 username keyemail key를 사용한다.

return (
    <div>
      {users.map(user => (
        <User user={user} key={user.id} />
      ))}
    </div>
  );

✔배열 users에 map 함수를 사용하여 각 원소를 순회한다. <User /> 컴포넌트에 userkey props를 준다. 그리고 각 배열 원소를 렌더링하여 리턴한다. map() 함수는 배열안에 있는 각 원소를 변환하여 새로운 배열을 만들어준다. 리액트에서 동적인 배열을 렌더링해야 할 때는 이 함수를 사용하여 일반 데이터 배열을 리액트 엘리먼트로 이루어진 배열로 변환해주면 된다.

리액트에서 key란, 렌더링 시 컴포넌트 배열에 어떤 변화가 일어났는지 더욱 빠르게 알아내기 위해 사용한다.

배열을 렌더링하게 될때는 key를 설정해야 비로소 효율적으로 렌더링할 수 있다. 그리고 고유값이 없는 경우에는 key라는 자리에 인덱스값을 넣을 수 있긴 하지만 그것은 비효율적이다.

배열의 항목 추가하기

onChange는 input값이 바뀌게될때 호출할 이벤트 처리함수고, onCreate는 버튼을 눌렀을때 새로운 항목을 등록해주는 함수다.
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;

function CreateUser({ username, email, onChange, onCreate }) { .... }
: input 값인 username, email 을 부모 컴포넌트에서 props로 넘겨 받아서 사용한다. onChange, onCreate 이벤트도 마찬가지로 props로 넘겨 받아서 사용한다.

<input name="username" placeholder="이름" onChange={onChange} value={username} />

: input 속성들을 설정해준다.

<button onClick={onCreate}>등록</button>
: 버튼을 클릭하면 배열에 새로운 항목이 추가되는 이벤트를 등록해준다.


UserList.js

import React from 'react';

function User({ user }) {
  return (
    <div>
      <b>{user.username}</b> <span>({user.email})</span>
    </div>
  );
}

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

export default UserList;

App.js

import React, { useRef, useState } from 'react';
import UserList from './UserList';
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: 'jenny',
      email: 'jenny@gmail.com'
    },
    {
      id: 2,
      username: 'user1',
      email: 'user1@gmail.com'
    },
    {
      id: 3,
      username: 'user2',
      email: 'user2@gmail.com'
    }
  ]);

  const nextId = useRef(4);
  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };
  return (
    <>
      <CreateUser
        username={username}
        email={email}
        onChange={onChange}
        onCreate={onCreate}
      />
      <UserList users={users} />
    </>
  );
}

export default App;

const [inputs, setInputs] = useState({ username: '', email: '' });
: useState로 inputs 상태를 관리해준다.

const { username, email } = inputs;
: inputs 원소를 디스트럭쳐링하여 추출한다.

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

: input 상태값이 바뀔때마다 inputs를 업데이트해준다. ... spread 연산자를 활용하여 inputs 객체 복사 후 상태값을 수정한다.

const [users, setUsers] = useState([
    {
      id: 1,
      username: 'jenny',
      email: 'jenny@gmail.com'
    }, ..... ])

: 원래 있던 user 배열 상태를 useState로 관리해준다.

const nextId = useRef(4);
: 새로 추가될 배열의 id 초기값으로 4를 준다.

const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };     

  //setUsers([...users, user]);
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };

: 등록 버튼을 누르면 실행되는 onCreate 함수에는 id 값을 갱신하고 새로운 유저 정보를 가져오는 user 객체를 만든다.

배열의 항목 제거하기

UserList.js

import React from 'react';

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

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

export default UserList;

function User({ user, onRemove })
function UserList({ users, onRemove })
: 두 컴포넌트 모두 onRemove 함수를 인자로 받는다.

<button onClick={() => onRemove(user.id)}>삭제</button>
: 삭제 버튼을 구현하고, 클릭 시 user.id가 인자로 들어간 onRemove 함수를 호출한다.

<User user={user} key={user.id} onRemove={onRemove} />
: onRemove 함수를 User 컴포넌트 props로 넣어준다.


import React, { useRef, useState } from 'react';
import UserList from './UserList';
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
    });
    console.log(inputs)
  };
  const onRemove = id => {
    setUsers(users.filter(user => user.id !== id))
  };
  const [users, setUsers] = useState([
    {
      id: 1,
      username: 'jenny',
      email: 'jenny@gmail.com'
    },
    {
      id: 2,
      username: 'user1',
      email: 'user1@gmail.com'
    },
    {
      id: 3,
      username: 'user2',
      email: 'user2@gmail.com'
    }
  ]);

  const nextId = useRef(4);
  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };

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

export default App;

const onRemove = id => { setUsers(users.filter(user => user.id !== id)) };
: onRemove 함수를 구현해준다. 배열 원소를 제거할 때도 마찬가지로 불변성을 유지해야하므로, 배열 filter 메소드를 사용한다. 삭제 버튼 클릭 시, 클릭된 id와 일치하지 않는 user.id 들을 필터링하여 다시 렌더링하는 것이다.
즉, 파라미터 id와 클릭된 user.id가 일치하면 삭제된다.

<UserList users={users} onRemove={onRemove} />
: 렌더링시 UserList에 props로 onRemove를 넘겨준다.

배열의 항목 수정하기

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

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

  const { username, email } = inputs;

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

  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))
  };

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

  const nextId = useRef(4);
  const onCreate = () => {
    const user = {
      id: nextId.current,
      username,
      email
    };
    setUsers(users.concat(user));

    setInputs({
      username: '',
      email: ''
    });
    nextId.current += 1;
  };

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

export default App;

const onToggle = id => {

    setUsers(users.map(user => 

      user.id === id ? { ...user, active: !user.active} : user))

  };

: 사용자 이름 누르면 active 값이 바뀌는 onToggle 함수를 구현해준다. setUsers로 상태관리를 해주고, map 메소드를 통해 파라미터 id와 user.id가 일치한다면 active 값을 반전시켜주고, 그렇지않으면 user 원소를 그대로 둔다.

<UserList users={users} onRemove={onRemove} onToggle={onToggle} />
: UserList props로 onToggle 함수를 넣어준다.


UserList.js

import React from 'react';

function User({ user, onRemove, onToggle }) {
  return (
    <div>
      <b style={
        {
          color: user.active ? 'green' : 'black',
          cursor: 'pointer'
        }
      }
      onClick={() => onToggle(user.id)}>
        {user.username}
      </b> 
      <span>({user.email})</span>
      <button onClick={() => onRemove(user.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;
 <b style={
        {
          color: user.active ? 'green' : 'black',
          cursor: 'pointer'
        }
      }

      onClick={() => onToggle(user.id)}>

        {user.username}
    </b> 

: 인라인 형식으로 스타일을 넣어준다. user.active 값이 true 이면 초록색, 아니면 검은색 컬러를 준다. 클릭하면 user.id를 인자로 받는 onToggle함수를 호출해준다.

만약 onClick={onToggle(user.id)} 이렇게 작성한다면 렌더링 시 함수가 무한히 호출되는 에러가 발생한다.

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

: props로 onToggle 함수를 받아오고 User 컴포넌트에도 전달해준다.

0개의 댓글