React 주말 #6

#6 침대에서 뒹글

useRef 로 특정 DOM 선택

자바스크립트에서는 우리가 특정 DOM을 선택해야할 때, getElementById, querySelector 같은 DOM selector 함수를 사용해야한다.

리액트에서 가끔식 DOM을 직접 선택해야 하는 상황이 올 수 있다.
이 때, 리액트에서 ref라는 것을 사용한다.

함수형 컴포넌트에서 ref 를 사용 할 때에는 useRef 라는 Hook 함수를 사용한다.

클래스형 컴포넌트에서는 우리가 콜백 함수를 사용하거나 React.createRef 라는 함수를 사용하는데 현재는 중요하지 않는다.

전에 만든 InputSample 초기화 버튼을 클릭했을 때 이름 input 에 포커스가 잡히도록 useRef 를 사용하여 기능을 구현하겠다!

InputSample.js

import React, { useState, useRef } from 'react';

function InputSample() {
  const [inputs, setInputs] = useState({
    name: '',
    nickname: ''
  });
  const nameInput = useRef();

  const { name, nickname } = inputs; // 비구조화 할당을 통해 값 추출

  const onChange = e => {
    const { value, name } = e.target; // 우선 e.target 에서 name 과 value 를 추출
    setInputs({
      ...inputs, // 기존의 input 객체를 복사한 뒤
      [name]: value // name 키를 가진 값을 value 로 설정
    });
  };

  const onReset = () => {
    setInputs({
      name: '',
      nickname: ''
    });
    nameInput.current.focus();
  };

  return (
    <div>
      <input
        name="name"
        placeholder="이름"
        onChange={onChange}
        value={name}
        ref={nameInput}
      />
      <input
        name="nickname"
        placeholder="닉네임"
        onChange={onChange}
        value={nickname}
      />
      <button onClick={onReset}>초기화</button>
      <div>
        <b>값: </b>
        {name} ({nickname})
      </div>
    </div>
  );
}

export default InputSample;

useRef() 를 사용하여 Ref 객체를 만들어주고, 이 객체를 우리가 선택하고 싶은 DOM 에 ref 값으로 설정해야한다.

이 때, Ref 객체의 .current 값은 우리가 원하는 DOM 을 가르킨다.

위의 코드에서 onReset 함수에서 input에 포커스를 하는 focus( ) API를 호출해줬다.

nameInput.current.focus();

결과는...

초기화를 눌렀을 때, 이름 input 태그에 focus가 들어온다.

그리고

const refContainer = useRef(initialValue);

useRef는 .current 프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환한다.

배열 렌더링하기

이번에는 리액트에서 객체가 아닌 배열을 렌더링하는 방법을 알아보겠다.

const users = [
  {
    id: 1,
    username: 'velopert',
    email: '[email protected]'
  },
  {
    id: 2,
    username: 'tester',
    email: '[email protected]'
  },
  {
    id: 3,
    username: 'liz',
    email: '[email protected]'
  }
];

이렇게 배열이 존재한다고 가정해보자!

어떻게하면은 이 배열을 컴포넌트로 렌더링할 수 있을까?
그냥 코드 그대로 작성을 하면되는데, 우리는 src 폴더에 UserList라는 파일을 생성해서 다음과 같이 컴포넌트를 생성한다.

UserList.js

import React from 'react';

function UserList() {
  const users = [
    {
      id: 1,
      username: 'velopert',
      email: '[email protected]'
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]'
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]'
    }
  ];
  return (
    <div>
      <div>
        <b>{users[0].username}</b> <span>({users[0].email})</span>
      </div>
      <div>
        <b>{users[1].username}</b> <span>({users[1].email})</span>
      </div>
      <div>
        <b>{users[2].username}</b> <span>({users[1].email})</span>
      </div>
    </div>
  );
}

export default UserList;

위에처럼 코드를 작성해봤는데, 다시 사용하는 코드를 일일히 넣는건 비효율적이라 컴포넌트를 재사용 할 수 있도록 새로 만들어보겠다.

UserList 파일을 다시 수정해주면...

아! 그리고 한 파일에 하나의 컴포넌트가 아니라 여러개의 컴포넌트를 선언해도 아무런 문제는 없다!

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: 'velopert',
      email: '[email protected]'
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]'
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]'
    }
  ];

  return (
    <div>
      <User user={users[0]} />
      <User user={users[1]} />
      <User user={users[2]} />
    </div>
  );
}

export default UserList;

이제 컴포넌트를 App 파일에서 렌더링해보겠다.

App.js

import React from 'react';
import UserList from './UserList';

function App() {
  return (
    <UserList />
  );
}

export default App;

결과는...

만약 배열이 100개 이상이면 하나하나 조회해가면서 렌더링하는 방법은 비효율적이라 이럴 때, 자바스크립트 배열의 내장함수인 map( )을 사용하면된다.

map() 함수는 배열안에 있는 각 요소를 변환하여 새로운 배열을 만들어준다.

리액트에서는 동적인 배열을 렌더링을 할 때는 map 함수를 사용하여 일반 데이터 배열을 리액트 엘리먼트로 이루어진 배열로 바꿔준다.

UserList 컴포넌트를 다음과 같이 수정해본다.

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: 'velopert',
      email: '[email protected]'
    },
    {
      id: 2,
      username: 'tester',
      email: '[email protected]'
    },
    {
      id: 3,
      username: 'liz',
      email: '[email protected]'
    }
  ];

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

export default UserList;

이렇게 작성하면 일일히 각 배열의 요소를 반복해가면서 쓰는게 아니라 한 번만 map 함수를 쓰면 모든 요소가 렌더링된다.

하지만 브라우저의 콘솔로 가보면 특정 에러가 보일 것 이다.

리액트에서는 배열을 렌더링할 때, key라는 props를 설정해줘야한다.
key 값은 각 요소들의 고유값을 설정해야 해서 위의 같은 경우에는 id가 고유 값이다.

key prop을 추가하면...

UserList.js

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

만약에 배열안에 고유값이 없다면, map 함수를 사용할 때 설정하는 콜백함수의 두 번째 파라미터인 index를 key로 설정해둬된다.

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

여기서 정리하자면...

map 함수에서 key가 필요한 이유는

  • Map에 key 값이없다면 중간의 값이 바뀌었을때 그 하위 값들이 전부 변하기 때문인다. key값을 사용한다면 key를 이용해 중간의 값을 추가하게 된다.

참고 : 벨로퍼트와 함께하는 모던 리액트

느낀점 :

  • 오늘은 useRef와 배열을 렌더링에 대해 알아보는 시간을 가졌습니다.
  • map 함수는 배열를 새롭게 리턴하기 때문에, 리액트에서 많이 사용되고 있고, useRef는 아직 헷갈리는 부분이 있을 수 있어서 다시 공부해야겠다.

좋은 웹페이지 즐겨찾기