[React] 컴포넌트 반복

강은비·2022년 1월 13일
0

React

목록 보기
16/36

react 스터디에서 리액트를 다루는 기술이라는 책을 선정했고 이 책을 읽고 배운 것을 바탕으로 작성되었다.


📌 배열 렌더링하기

Array.prototype.map(callback(currentValue, index, array), thisArg)

  • 배열 각 요소에 대해 콜백함수를 실행하고 실행결과를 모은 새 배열을 리턴한다.
  • currentValue: 현재 처리할 요소 값
  • index: 현재 요소의 인덱스 값
  • array: 메서드를 호출한 배열
  • thisArg: 콜백함수를 실행할 때 사용할 this
function User({user}){
    return(
        <div>
            <span>{user.username}</span>
            <span>({user.email})</span>
        </div>
    );
}

function UserList(){
    const users = [
    {
      id: 1,
      username: 'velopert',
      email: 'public.velopert@gmail.com'
    },
    {
      id: 2,
      username: 'tester',
      email: 'tester@example.com'
    },
    {
      id: 3,
      username: 'liz',
      email: 'liz@example.com'
    }
  ];
  return (
      <div>
          users.map(user => <User user = {user} key = {user.id}/>);
      </div>
  );
}

export defalut UserList;

📙 key

💡 Key는 React가 어떤 항목을 변경, 추가 또는 삭제할지 식별하는 것을 돕는다.

key 설정

  • key 값은 다른 항목들 사이에서 해당 항목을 고유하게 식별할 수 있는 값으로 설정해야 한다.
  • 만약 고유값이 없다면 index 값을 key값으로 사용하면 된다.
  • key는 배열 안 형제 사이에서 고유해야 하고 전체 범위에서 고유할 필요는 없다.

key 의 존재유무에 따른 업데이트 방식

const array = ['a', 'b', 'c', 'd'];
array.map(item => <div>{item}</div>);
  • 위 배열의 b 와 c 사이에 z 를 삽입하게 된다면, 리렌더링을 하게 될 때 <div>b</div> 와 <div>c</div> 사이에 새 div 태그를 삽입을 하게 되는 것이 아니라, 기존의 c 가 z 로바뀌고, d 는 c 로 바뀌고, 맨 마지막에 d 가 새로 삽입된다.
  • 그 다음에 a 를 제거하게 된다면, 기존의 a 가 b 로 바뀌고, b 는 z 로 바뀌고, z는 c로 바뀌고, c는 d 로바뀌고, 맨 마지막에 있는 d 가 제거된다.
const array = [
    {
        id: 0,
        text: 'a'
    },
    {
        id: 1,
        text: 'b'
    },
    {
        id: 2,
        text: 'c'
    },
    {
        id: 3, 
        text: 'd'
    }

];
array.map(item => <div key={item.id}>{item.text}</div>);
  • 수정되지 않는 기존의 값은 그대로 두고 원하는 곳에 내용을 삽입하거나 삭제한다.
  • 때문에, 배열을 렌더링 할 때에는 고유한 key 값이 있는 것이 중요하다.
  • 만약에 배열 안에 중복되는 key 가 있을 때에는 렌더링 시에 오류 메시지가 콘솔에 나타나게 되며, 업데이트가 제대로 이루어지지 않게 된다.

📌 배열에 항목 추가하기

  • 배열에 항목을 추가하는 방법
    • spread 연산자 사용
    • concat  메서드 사용
      • 기존의 배열을 수정하지 않고, 새로운 원소가 추가된 새로운 배열을 반환함.
  • 컴포넌트는 내부의 stateprops가 변경될 때마다 re-rendering 되는데, 함수형 컴포넌트는 내부에 정의된 로컬 변수들을 초기화한다.
  • 반면 useRef로 만들어진 객체는 React가 만든 전역 저장소에 저장되기 때문에 리렌더링되더라도 마지막으로 업데이트한 current 값이 유지된다.
// UserList.js
function User({user}){
    return(
        <div>
            <span>{user.username}</span>
            <span>({user.email})</span>
        </div>
    );
}

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

export defalut UserList;
// CreateUser.js
function CreateUser({username, email, onChange, onCreate}){
    return (
       <div>
          <input
              type="text",
              name="username",
              value={username}
              onChange={onChange}
              placeholder="계정명"/>
          <input
              type="email"
              name="email"
              value={email}
              onChange={onChange}
              placeholder="이메일"/>
          <button onClick = {onCreate}>등록</button>
       </div>
    );
}
export default Createuser;
// 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 [users, setUsers] = userState([
        {
          id: 1,
          username: 'velopert',
          email: 'public.velopert@gmail.com'
        },
        {
          id: 2,
          username: 'tester',
          email: 'tester@example.com'
        },
        {
          id: 3,
          username: 'liz',
          email: 'liz@example.com'
        }
    ]);
    
    const nextId = useRef(4);
    
    const onChange = (e) => {
        const { name, value } = e.target;
        setInputs({
            ...inputs,
            [name]: value
        });
    };
    const onCreate = () => {
        const newUser = {
            id: nextId.current;
            username,
            email
        };
        
        setUsers([...user, newUser ]);  // spread 연산자 이용
        // setUsers(user.concat(newUser));   // concat 메서드 이용
        
        setInputs({
            username: "",
            email: ""
        });
        nextId.current++;
    }
    
    return (
       <div>
          <CreateUser 
              username={username} 
              email={email} 
              onChange={onChange}
              onCreate={onCreate}
          />
          <UserList users={users} />
       </div>
    );
}
export default Createuser;

📌 배열에 항목 제거하기

Array.prototype.filter(callback(currentValue, index, array), thisArg)

  • 각 배열 요소에 대해 콜백함수를 실행하고 실행 결과가 true인 요소들만 모은 새 배열을 반환한다.
  • currentValue: 현재 처리할 요소 값
  • index: 현재 요소의 인덱스 값
  • array: 메서드를 호출한 배열
  • thisArg: 콜백함수를 실행할 때 사용할 this
// UserList.js
function User({user, onRemove}){
    return(
        <div>
            <span>{user.username}</span>
            <span>({user.email})</span>
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

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

export defalut 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 [users, setUsers] = userState([
        {
          id: 1,
          username: 'velopert',
          email: 'public.velopert@gmail.com'
        },
        {
          id: 2,
          username: 'tester',
          email: 'tester@example.com'
        },
        {
          id: 3,
          username: 'liz',
          email: 'liz@example.com'
        }
    ]);
    
    const nextId = useRef(4);
    
    const onChange = (e) => {
        const { name, value } = e.target;
        setInputs({
            ...inputs,
            [name]: value
        });
    };
    const onCreate = () => {
        const newUser = {
            id: nextId.current;
            username,
            email
        };
        
        setUsers([...users, newUser ]);  // spread 연산자 이용
        // setUsers(users.concat(newUser));   // concat 메서드 이용
        
        setInputs({
            username: "",
            email: ""
        });
        nextId.current += 1;
    }
    
    const noRemove = (id) => {
        setUsers(users.filter(user => user.id !== id));
    }
    
    return (
       <div>
          <CreateUser 
              username={username} 
              email={email} 
              onChange={onChange}
              onCreate={onCreate}
          />
          <UserList users={users} onRemove={onRemove}/>
       </div>
    );
}
export default Createuser;
  • onRemove라는 이벤트 핸들러를 계속 props로 전달하여 최종적으로 User 컴포넌트에 전달
  • <button onClick={() => onRemove(user.id)}>삭제</button>

📌 배열에 항목 수정하기

User 컴포넌트에 계정명을 클릭했을때 색상이 초록색으로 바뀌고, 다시 누르면 검정색으로 바뀌도록 구현

// UserList.js
function User({user, onRemove, onToggle}){
    return(
        <div>
            <span 
                onClick={() => onToggle(user.id)}
                style = {{
                    cursor: "pointer",
                    color: user.active ? "green" : "black"
                }}
            >{user.username}</span>
            <span>({user.email})</span>
            <button onClick={() => onRemove(user.id)}>삭제</button>
        </div>
    );
}

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

export defalut 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 [users, setUsers] = userState([
        {
          id: 1,
          username: 'velopert',
          email: 'public.velopert@gmail.com',
          active: true
        },
        {
          id: 2,
          username: 'tester',
          email: 'tester@example.com',
          active: false
        },
        {
          id: 3,
          username: 'liz',
          email: 'liz@example.com',
          active: false
        }
    ]);
    
    const nextId = useRef(4);
    
    const onChange = (e) => {
        const { name, value } = e.target;
        setInputs({
            ...inputs,
            [name]: value
        });
    };
    const onCreate = () => {
        const newUser = {
            id: nextId.current;
            username,
            email
        };
        
        setUsers([...users, newUser ]);  // spread 연산자 이용
        // setUsers(users.concat(newUser));   // concat 메서드 이용
        
        setInputs({
            username: "",
            email: ""
        });
        nextId.current += 1;
    }
    
    const onRemove = (id) => {
        setUsers(users.filter(user => user.id !== id));
    }
    
    const onToggle = (id) => {
        setUsers(users.map(user => 
            user.id === id ? {...user, user.active: !user.active} : user
        );
    };
    
    return (
       <div>
          <CreateUser 
              username={username} 
              email={email} 
              onChange={onChange}
              onCreate={onCreate}
          />
          <UserList users={users} onRemove={onRemove} onToggle={onToggle}/>
       </div>
    );
}
export default Createuser;

참고

0개의 댓글