ABOUT ME

-

Today
-
Total
-
  • React Array CRUD : 배열 렌더링+추가+제거+수정
    Study/React 2020. 1. 7. 10:11
    반응형

     

     

    React에서 배열을 렌더링 하려면 어떻게 해야할까?

    우선 배열 원소를 하나 하나 렌더링해야하기 때문에 재사용되는 코드가 있을 것이다.

    따라서 배열 컴포넌트 외에 렌더링에 필요한 컴포넌트를 하나 더 생성한다.

    그리고 동적인 데이터 처리를 위해 자바스크립트의 내장함수 map() 을 사용하여 동적인 배열을 렌더링해준다.

    마지막으로 배열을 렌더링 할 때는 key라는 props를 설정해야한다.

    key는 배열의 각 원소들이 가지고 있는 고유값으로 설정해줘야한다.

    key가 있어야만 배열이 업데이트 될 때 효율적으로 렌더링 될 수 있기 때문이다.

     

     

    React element의 유일성

    만약 각 요소에 key를 설정해주지 않으면 아래와 같은 오류 메세지가 뜬다.

    "Each child in a list should have a unique "key" prop."

    React element들은 유일해야하는 특징을 가지고 있으므로 각각 props에 key를 설정하여

    각 요소에 정체성을 부여해주는 작업이 필요하다.

    만약 배열의 각 원소마다 고유값이 없다면 map() 함수를 쓸 때 파라미터 index를 활용하여 key로 설정하기도 하지만,

    index를 key로 사용하는 것은 좋은 방법은 아니다.

    배열 요소가 추가만 되는 경우에는 index를 활용해도 되지만, 배열을 삭제하는 경우도 있다면 index를 사용하면 안된다.

    가장 좋은 방법은 고유값을 직접 만들어서 넣어주는게 가장 좋다.

     

     

    React 배열 렌더링 방법

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

     

     

    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: 'subin',
          email: 'subin@example.com'
        },
        {
          id: 2,
          username: 'user1',
          email: 'user1@example.com'
        },
        {
          id: 3,
          username: 'user2',
          email: 'user2@example.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}를 파라미터에 받아온다.

      각 배열 원소의 username key와 email key를 사용한다.

     

     

    return ( 

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

      </div> ); 

     

    : 배열 users 에 map 함수를 사용하여 각 원소를 순회한다.

      <User /> 컴포넌트에 user와 key props를 준다.

      그리고 각 배열 원소를 렌더링하여 리턴한다.

     

     

    React 배열 원소 추가 방법

     

    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: 'subin',
          email: 'subin@example.com'
        },
        {
          id: 2,
          username: 'user1',
          email: 'user1@example.com'
        },
        {
          id: 3,
          username: 'user2',
          email: 'user2@example.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: 'subin',
          email: 'subin@example.com'
        }, ..... ])

     

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

     

      const nextId = useRef(4);

     

    : 새로 추가될 배열의 id 초기값으로 4를 준다.


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

        setUsers(users.concat(user));

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

     

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

     

    setUsers 함수를 통해 기존 배열 users에 새로운 항목 user 변수를 합친다. 이 때 배열 메소드 concat을 사용한다.

    불변성을 유지해야하기 때문에 push와 같은 메소드는 사용하면 안된다.

    concat 메소드 말고 spread 연산자를 활용하면 아래와 같다.

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

    이는 users 배열을 복사하고 새로운 user 항목을 추가하는 방법이다.

     

    setInputs 함수를 통해 input 값을 공백으로 초기화 시켜주고, 다음 id 값에 +1을 해준다.

     

     

      return (

        <>

          <CreateUser

            username={username}

            email={email}

            onChange={onChange}

            onCreate={onCreate}

          />

          <UserList users={users} />

        </>

      );

     

    : App.js를 렌더링 하는데 컴포넌트는 CreateUser와 UserList가 필요하다.

    CreateUser와 UserList에는 props로 필요한 값들을 넘겨준다.

     

     

    React 배열 원소 제거 방법

    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로 넣어준다.

     

    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
        });
        console.log(inputs)
      };
      const onRemove = id => {
        setUsers(users.filter(user => user.id !== id))
      };
      const [users, setUsers] = useState([
        {
          id: 1,
          username: 'subin',
          email: 'subin@example.com'
        },
        {
          id: 2,
          username: 'user1',
          email: 'user1@example.com'
        },
        {
          id: 3,
          username: 'user2',
          email: 'user2@example.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를 넘겨준다.

     

     

    React 배열 원소 수정 방법

    App.js

    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 컴포넌트에도 전달해준다.

     

     

    반응형

    댓글