React Fragments, Portals, Refs

·2022년 6월 24일
0

react

목록 보기
6/24
post-thumbnail

fragment

JSX의 취약점

JSX 는 항상 모든 태그를 하나로 감싸는 컨테이너 태그가 하나만 있어야 합니다. 이는 리액트가 하나의 컴포넌트만을 리턴해야 때문입니다. 여기서 문제점이 나타나는데, 불 필요한 컨테이너 태그가 생길 수 있다는 것 입니다. JSX를 규칙을 따르다가 아래와 같은 코드가 만들어질 수도 있지 않을까요???

<div>
  <div>
  	<div>
  	  <h1>Hello world!</h1>
  	</div>
  </div>
</div>

fragment 적용하기

fragment 사용은 매우 간단합니다. 기존의 컨테이너 태그 대신<React.Fragment> 태그를 만들어주면 됩니다. 단축 문법으로 <> 로 쓰는 것도 가능합니다.

import { Fragment } from "react";
const App = () => {
  return (
    <Fragment>
      <h1>Hello world</h1>
    </Fragment>
    // <>
    //  <h1>Hello world</h1>
    // </>
  );
}


Portal

portal을 사용해야하는 이유

const AddUser = (props) => {
...
...

  return (
      <div>
        {error && <ErrorModal title={error.title} message={error.message} onConfirm={errorHandler} />}
        <Card className={classes.input}>
          <form onSubmit={addUserHandler}>
            <label htmlFor="username">Username</label>
            <input
              id="username"
              type="text"
              value={enteredUserName}
              onChange={userNameChangeHandler}
            />
            <label htmlFor="age">Age (Years)</label>
            <input id="age" type="number" value={enteredAge} onChange={ageChangeHandler} />
            <Button type="submit">Add User</Button>
          </form>
        </Card>
      </div>
    );
};

위의 코드는 <form> 이벤트가 적용된 컴포넌트입니다. 이름과 나이가 들어가는 경우 유효성 검사를 통해 에러 모달을 던져줍니다. 저 상태에서도 프로그램을 쓰는데는 큰 문제는 없겠지만, 어찌되었든 좋은 코드라고 보기 어려운 현상이 나타납니다.

JSX 취약점2

우리가 자주 쓰는 모달 페이지는 보통 <body> 태그 전체를 오버레이하여 모달을 정중앙 혹은 위쪽에 배치하여 나타내곤 합니다. 그렇기 때문에 보통 배치를 <header> 혹은 <body> 태그 위에 배치하곤 하죠. 하지만 JSX 모든 태그는 root 컨테이너 태그의 아래에 놓이기 때문에 기존의 배치 방식을 적용할 수 없는 현상이 발생합니다.

portal 적용하기

portal 은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하게 만들어줍니다.
처음 리액트 프로젝트를 생성하는 경우, index.js 에는 기본적으로 React.DOM을 통해 root 컴포넌트를 렌더링하고 있을 겁니다. portal 역시 이와 비슷한 방법으로 적용할 수 있습니다. 먼저 배치 할 새로운 루트 태그를 public 폴더의 html 파일에 넣어줍시다. 중요성을 강조하기 위해 id 값도 함께 설정해줍시다.

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="backdrop-root"></div>
    <div id="overlay-root"></div>
    <div id="root"></div>
   	...
    ...
</body>

그 이후 새로운 컴포넌트를 생성해주고 ReactDOMimport 하여 ReactDOM.createPortal() 함수를 호출해주면 됩니다.
ReactDOM.createPortal() 함수에는 2개의 인자를 받습니다. 이는 index.js 파일에 기본적으로 존재하는, ReactDOM.render() 함수와 동일합니다.

첫번째 인자 : 컴포넌트
두번째 인자 : index.html 의 루트 DOM

const ErrorModal = (props) => {
  return (
    <>
      {ReactDOM.createPortal(
        <Backdrop onConfirm={props.onConfirm} />,
        document.getElementById("backdrop-root")
      )}
      {ReactDOM.createPortal(
        <ModalOverLay title={props.title} message={props.message} onConfirm={props.onConfirm} />,
        document.getElementById("overlay-root")
      )}
    </>
  );
};


ref

state의 취약점

리액트에서 훅을 통해 컴포넌트의 데이터를 변경하거나 읽어오기 위해 state 를 사용합니다. state 의 단점은 뭐가 있을까요??? 우선 단순히 읽어오기만 하면 되는 태그의 value 조차도 일일이 읽기와 변경을 모두 설정해주어야 한다는 겁니다. 또한, state는 해당 태그의 값이 변경될 때 마다 state 의 데이터가 변경, 저장, 변경, 저장을 반복합니다. 이러한 문제들을 개선하는 것이 바로 리액트 내에서 DOM API에 직접 접근 하는 refs 입니다.

ref 적용하기

state 마찬가지로 ref 에는 useRef() 라는 함수가 존재합니다. DOM 을 통해 직접적으로 값을 가져올 태그에 ref 속성을 정의해준 후, useRef() 와 연결해주면 됩니다.


const AddUser = (props) => {
  const nameInputRef = useRef();
  const ageInputRef = useRef();
  ...
  ...
  
  const enteredName = nameInputRef.current.value;
  const enteredUserAge = ageInputRef.current.value;
  
  console.log(enteredName, enteredUserAge);
  
  return (
    <>
      {error && <ErrorModal title={error.title} message={error.message} onConfirm={errorHandler} />}
      <Card className={classes.input}>
        <form onSubmit={addUserHandler}>
          <label htmlFor="username">Username</label>
          <input
            id="username"
            type="text"
            // value={enteredUserName}
            // onChange={userNameChangeHandler}
            ref={nameInputRef}
          />
          <label htmlFor="age">Age (Years)</label>
          <input
            id="age"
            type="number"
            // value={enteredAge}
            // onChange={ageChangeHandler}
            ref={ageInputRef}
          />
          <Button type="submit">Add User</Button>
        </form>
      </Card>
    </>
  );
}

state vs ref

라이프 사이클의 내에서 빈번한 변경을 알 필요가 없고, 마지막으로 주어진 값만 잘 읽어올 수 있다면 ref 가 좋은 대안일 것 입니다. 리액트와 상태 관리를 통해 데이터가 어떻게 변경되는 과정 모두를 알 필요가 있고, 데이터의 불변성을 침해하지 않는 선에서 데이터를 관리하기를 원한다면 state가 나을 거라고 생각합니다. ref 도 데이터를 변경하는 것이 사실 가능하지만 직접적인 접근을 통한 데이터의 변경은 코드를 어지럽히기 마련이니까요.

추가적으로 리액트의 상태관리에 의해 데이터가 제어되는 컴포넌트를 controlled-component 라 하며, 리액트의 라이프 사이클 권한 밖에서 개발자가 직접 DOM을 통해 데이터가 관리되는 컴포넌트를 uncontrolled-component 라고 합니다.

profile
새로운 것에 관심이 많고, 프로젝트 설계 및 최적화를 좋아합니다.

0개의 댓글