React 알아가기 (4)

삔아·2023년 5월 7일
0
post-thumbnail

해당 내용은 https://www.udemy.com/course/best-react/ 강의를 들으며 정리하고 스스로 공부 한 내용을 기록 하였습니다.

Section 4. Fragments, Portals & Refs

JSX코드 한계점

리액트를 천천히 배워가고 있는 시점에서 한가지 의문점이 생겼다.

return (
	<h2>Hi there! </h2>
  	<p>This does not work </p>
);

해당 코드처럼 작성할 순 없는걸까? 실제로 작성해보면 빨간 줄 표시가 뜨면서 root Element 가 필요하다고 나온다.

return (
	<div>
   		<h2> Hi there! </h2>
		<p> This does not work :-( </p>
	</div>
);

이처럼 div 태그 혹은 사용자가 정의한 컴포넌트 등의 어떤 요소를 감싸주어야 정상적으로 작동이 된다.

그러면 각각의 컴포넌트가 전부 div 태그로 감싸주게 된다면..?

<div>
  <div>
    <div>
      <div>
        ...
      </div>
    </div>
  </div>
</div>

이렇게 렌더링 되었을 때에 div의 늪에 빠지지 않게 되지 않을까 라는 한계점을 확인 할 수 있다.

Wrapper component

이에 대한 한계점을 우리는 Wrapper Component 를 만들어 해소 시킬 수 있다.

const Wrapper = (props) => {
  return props.children;
};

export default Wrapper;

이러한 컴포넌트를 만들어주어서 div 태그의 늪에 대해 빠져나갈 수 있다.

React.Fragment

위의 방법처럼 Wrapper component 를 만들어주어 사용해도 되지만, 리액트에서 지원을 해주고 있는데, 그 중 하나는 바로 <React.Fragment> 이다. 혹은 <> </> 를 이용하여도 된다.

import React, { useState, Fragment } from "react";

function App() {
	...
    
  return (
    <Fragment>
      ...
    </Fragment>
  );
}

export default App;

React Portals

하지만 위의 방법에서는 아쉬운 부분이 있는데, 바로 전체 페이지에 대한 오버레이를 하는 모달에 있어서 아쉬운 부분이 있다.

return (
	<React.Fragment>
    	<MyModal />
    	<MyInputForm />
    </React.Fragment>
);

해당 코드는 문제가 없으며 실제로 화면을 작동할 때에도 문제가 없어보일 수 있지만, HTML 구조로 보았을 때 (시멘틱구조) 좋은 구조는 아니다.

// Real DOM
<section>
  <h2> Content ... </h2>
  <div class="my-modal">
    <h2> Modal ... </h2>
  </div>
  <form>
    <label> Username </label>
    <input type="text" />
  </form>
</section>

모달은 전체 페이지에 대한 오버레이다. 모든 것 위에 있는 것을 의미하는데, 스타일링이나 접근성의 관점에서도 문제가 생길 수 있다.
만약 오버레이 내용이 중첩 되어 있다면 스크린 리더가 렌더링 되는 HTML코드를 일반적인 오버레이라고 인식하지 못할 수 있다. 또한 의미적인 관점이나 구조적인 관점으로 보았을 떄 에도 HTML 코드 안 깊은 곳에 자리 잡고 있다.

이러한 부분을 Portals 를 이용해서 해소 시킬 수 있다. 포탈을 이용하여 해당 부분을 해소 시켜보자.

포탈은 우선적으로, 컴포넌트를 이동시킬 장소와 그 후에 컴포넌트 에게 그 곳에 포탈을 가져야 한다고 알려줄 필요가 있다.

import ReactDOM from "react-dom";

return (
	<React.Fragment>
    {ReactDOM.createPortal()}
  </React.Fragment>
);

ReactDOM.creatProtal()

  1. 처음은 렌더링 되어야 하는 리액트 노드 - JSX 코드여야만 한다
  2. 두번째 인수는 포인터 - 렌더링 되어야하는 실제 DOM의 컨테이너를 가리키는 포인터
    document.createElementId() 를 작성한다. 실제 HTML DOM요소에 접근 가능하다.
import React from "react";
import ReactDOM from "react-dom";

const Backdrop = (props) => {
  return <div className={classes.backdrop} onClick={props.onConfirm} />;
};

const ModalOverlay = (props) => {
  return (
    <Card className={classes.modal}>
      <header className={classes.header}>
        <h2>{props.title}</h2>
      </header>
      <div className={classes.content}>
        <p>{props.message}</p>
      </div>
      <footer className={classes.actions}>
        <Button onClick={props.onConfirm}>Okay</Button>
      </footer>
    </Card>
  );
};

const ErrorModal = (props) => {
  return (
    <React.Fragment>
      {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")
      )}
    </React.Fragment>
  );
};

export default ErrorModal;

💡 포털의 핵심은 렌더링 된 HTML요소를 다른 곳으로 옮기는 것이다.

Ref

기본적으로 다른 DOM 요소에 접근해서 그것들로 작업을 할 수 있게 해주는 도구이다.

마지막에 렌더링 되는 HTML요소들과 다른 자바스크립트 코드의 연결한다.
-> useRef훅 이용 - 함수형 컴포넌트 안에서만 사용 가능하다.
어떤 HTML 요소라도 참조 중 하나에 연결 할 수 있다.
항상 객체, 항상 current프롭을 가지고 있으며, ref의 실제 값 갖게 된다.

const nameInputRef = useRef();
const ageInputRef = useRef();

// ex
nameInputRef.current.value

데이터 값 만 빠르게 읽고 싶으며 변경 계획이 없다면 ref를 이용하는 것도 좋은 방법이다.

참고

제어되는 컴포넌트와 제어되지 않는 컴포넌트

보통 인풋 필드에서 많이 이야기 하는데, 리액트에서 제어를 하냐/ 안하냐 에 따라 용어가 바뀐다.
state 를 사용했을 때는 입력을 할 떄 마다 state에 저장을 하고 … value는 state에서 값을 꺼내오고.. 이와 같은 부분을 제어 된 컴포넌트라고 이야기 하지만, ref 같은 경우에는 제어되지 않는 컴포넌트라고 말을 한다.

profile
Frontend 개발자 입니다, 피드백은 언제나 환영 입니다

0개의 댓글