[React] Custom Component, useRef

노호준·2023년 2월 20일
0

CDD

  • Component-Driven Development
  • 디자인과 개발단계에서부터 재사용할수있는 UI컴포넌트를 미리 디자인하고 개발한다면 좋겠다.
  • 레고처럼 조립해 나갈 수 있는 부품단위로 UI컴포넌트를 만들어나가는 개발을 진행할 수 있다.

CSS

  • 인터넷이 만들어진후 CSS에는 패턴이 부족해 구조화된 CSS를 만드는법이 연구됐다.

  • CSS전처리기: CSS문제점들을 프로그래밍 개념을 활용하여 해결해나가는 방법

  • SASS는 CSS를 확장해주는 스크립팅 언어

  • CSS방법론이 대두됐다. 코드의 재사용,간결화,확장성 그러나 캡슐화는 어려웠다.

  • 결국 CSS도 컴포넌트 영역으로 불러들이기 위해 CSS in JS가 탄생했다.

Styled Components

  • CSS를 컴포넌트화 시킴으로서 해결해주는 라이브러리
  • 리액트+스타일드컴포넌트로 하나의 JS파일에서 모든걸 개발할 수 있다.

1. 컴포넌트 만들기

import styled from "styled-components";

//Styled Components로 컴포넌트를 만들고
const BlueButton = styled.button`
  background-color: blue;
  color: white;
`;

export default function App() {
  // React 컴포넌트를 사용하듯이 사용하면 됩니다.
  return <BlueButton>Blue Button</BlueButton>;
}
  • 컴포넌트를 선언한 후 styled.태그종류 를 할당하고, 백틱안에 기존에 CSS를 작성하던 문법과 똑같이 스타일 속성을 작성한다.
  • 이렇게 만든 컴포넌트를 리액트 컴포넌트를 작성하듯 리턴문안에 작성해주면 스타일이 적용된 컴포넌트가 렌더된다.

2. 컴포넌트를 재활용해서 새로운 컴포넌트 만들기

  • 컴포넌트 선언하고 styled()에 재활용할 컴포넌트를 전달, 추가하고픈 스타일 속성을 작성
import styled from "styled-components";

const BlueButton = styled.button`
  background-color: blue;
  color: white;
`;

//만들어진 컴포넌트를 재활용해 컴포넌트를 만들 수 있습니다.
const BigBlueButton = styled(BlueButton)`
  padding: 10px;
  margin-top: 10px;
`;

//재활용한 컴포넌트를 재활용할 수도 있습니다.
const BigRedButton = styled(BigBlueButton)`
  background-color: red;
`;

export default function App() {
  return (
    <>
      <BlueButton>Blue Button</BlueButton>
      <br />
      <BigBlueButton>Big Blue Button</BigBlueButton>
      <br />
      <BigRedButton>Big Red Button</BigRedButton>
    </>
  );
}

3. Props 활용하기

  • styled component로 만든 컴포넌트도 React 컴포넌트처럼 props를 내려줄 수 있습니다.

  • 내려준 props값에 따라 컴포넌트를 렌더링하는것도 가능합니다.

  • 템플릿리터럴 문법을 사용하여 js코드를 사용할 수 있다. props를 받아오려면 props를 인자로 받는 함수를 만들어 사용하면 된다.
    1) props로 조건부 렌더링하기

  • 버튼 컴포넌트에 프롭스있으면 배경색으로 하늘색, 없으면 하얀색
    2) props값으로 렌더링하기

  • props.color가 있다면 그 색을, 없으면 하양을

4. 전역스타일 설정하기

  • 전역에 스타일을 설정하고 싶을땐?
    우선 임포트
    import { createGlobalStyle } from "styled-components";
    그다음 함수를 사용해 CSS파일에서 작성하듯 설정해주고픈 스타일을 작성한다.
const GlobalStyle = createGlobalStyle`
	button {
	padding : 5px;
    margin : 2px;
    border-radius : 5px;
	}

이렇게 만들어진 globalstyle컴포넌트를 최상위 컴포넌트에서 사용해주면 전역에 적용된다.

function App() {
	return (
		<>
			<GlobalStyle />
			<Button>전역 스타일 적용하기</Button>
		</>
	);
}

Storybook

  • UI 개발도구
  • 컴포넌트 탐색기중 하나
  • CDD위한 도구, 각 컴포넌트를 따로 볼 수 있게 해줌
  • 컴포넌트를 시뮬레이션, 문서화
  • Storybook은 기본적으로 독립적인 개발 환경에서 실행된다. 개발자는 애플리케이션의 다양한 상황에 구애받지 않고 UI 컴포넌트를 집중적으로 개발할 수 있다.

useRef

  • 리액트 애플리케이션 만들때 DOM직접 조작은 지양해야 한다.
  • 하지만 DOM을 꼭 건드려야 하는 상황이 온다. 이럴때 useRef라는 Hook함수를 쓴다.
  • 리액트로 대부분의 프론트엔드 요구사항 충족되나 DOM이 필요한 경우가 있다.
  • useRef 를 활용하면 DOM 노드, 엘리먼트, 그리고 React 컴포넌트 주소값을 참조할 수 있다.
  1. focus
  2. text selection
  3. media playback
  4. 애니메이션 적용
  5. d3.js, greensock등 DOM기반 라이브러리 활용
  • React는 이런 예외적인 상황에서 useReffh DOM노드, 엘리먼트, React컴포넌트 주소값 참조 가능
  • 아래 예시코드처럼 작성하면 주소값 활용할 수 있다.
const 주소값을_담는_그릇 = useRef(참조자료형)
// 이제 주소값을_담는_그릇 변수에 어떤 주소값이든 담을 수 있습니다.
return (
    <div>
      <input ref={주소값을_담는_그릇} type="text" />
        {/* React에서 사용 가능한 ref라는 속성에 주소값을_담는_그릇을 값으로 할당하면*/}
        {/* 주소값을_담는_그릇 변수에는 input DOM 엘리먼트의 주소가 담깁니다. */}
        {/* 향후 다른 컴포넌트에서 input DOM 엘리먼트를 활용할 수 있습니다. */}
    </div>);
  • 이 주소값은 컴포넌트가 re-render 되더라도 바뀌지 않습니다. 이 특성을 활용하여 아래의 제한된 상황에서 useRef를 활용할 수 있습니다.
function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>);
}
  • 제시된 상황 제외한 대부분의 경우 React 문법을 벗어나 useRef를 남용하는 것은 부적절하고, React의 특징이자 장점인 선언형 프로그래밍 원칙과 배치되기 때문에, 조심해서 사용해야 한다.
  • 아래 예제는

focus

import React, { useRef } from "react";

const Focus = () => {
  const firstRef = useRef(null);
  const secondRef = useRef(null);
  const thirdRef = useRef(null);

  const handleInput = (event) => {
    console.log(event.key, event);
    if (event.key === "Enter") {
      if (event.target === firstRef.current) {
        secondRef.current.focus();
        event.target.value = "";
      } else if (event.target === secondRef.current) {
        thirdRef.current.focus();
        event.target.value = "";
      } else if (event.target === thirdRef.current) {
        firstRef.current.focus();
        event.target.value = "";
      } else {
        return;
      }
    }
  };

  return (
    <div>
      <h1>타자연습</h1>
      <h3>각 단어를 바르게 입력하고 엔터를 누르세요.</h3>
      <div>
        <label>hello </label>
        <input ref={firstRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>world </label>
        <input ref={secondRef} onKeyUp={handleInput} />
      </div>
      <div>
        <label>codestates </label>
        <input ref={thirdRef} onKeyUp={handleInput} />
      </div>
    </div>
  );
};

export default Focus;

media playback

import { useRef } from "react";

export default function App() {
  const videoRef = useRef(null);

  const playVideo = () => {
    videoRef.current.play();
    console.log(videoRef.current);
  };

  const pauseVideo = () => {
    videoRef.current.pause();
    videoRef.current.remove();
  };

  return (
    <div className="App">
      <div>
        <button onClick={playVideo}>Play</button>
        <button onClick={pauseVideo}>Pause</button>
      </div>
      <video ref={videoRef} width="320" height="240" controls>
        <source
          type="video/mp4"
          src="https://player.vimeo.com/external/544643152.sd.mp4?s=7dbf132a4774254dde51f4f9baabbd92f6941282&profile_id=165"
        />
      </video>
    </div>
  );
}

0개의 댓글