리액트 숙련 - Styling과 React Hook (useState, useEffect, useRef ..

새벽로즈·2023년 11월 7일
1

TIL

목록 보기
40/72
post-thumbnail

CSS-in-JS

  • 자바스크립트 코드로 CSS를 작성하여 컴포넌트를 꾸미는 방식
  • 순수한 CSS 코드 대신 자바스크립트를 사용해 동적으로 스타일을 생성함

styled-components

  • 리액트에서 CSS-in-JS 방식으로 컴포넌트를 스타일링하는 패키지
  • 많은 개발자들에게 선호되는 도구 중 하나

패키지

  • React에 기본으로 포함되지 않은 기능을 제공하는 써드파티 프로그램으로, npm이나 yarn을 통해 설치하여 사용함

styled-components 설치하기

  1. vscode에서 설치하기

위의 이미지의 익스텐션을 설치하기

  1. 터미널에서 입력하기
yarn add styled-components

styled-components 사용해보기

  1. 먼저 스타일링할 수 있도록 만들기
const StBox = styled.div`
  width: 100px;
  height: 100px;
  border: 1px solid red;
  margin: 20px;
`;

☞ 먼저 const 선언 해주고 ``(백틱)을 써서 그 안에 CSS를 넣어준다.

  1. 적용할 곳에 적용해주기
function App() {
  return <StBox>안녕하세요</StBox>;
}

조건부 스타일링

import styled from "styled-components";

const StBox = styled.div`
  width: 100px;
  height: 100px;
  border: 1px solid ${(props) => props.borderColor};
  margin: 20px;
`;

const StP = styled.p`
  color: purple;
`;

function App() {
  return (
    <>
      <StBox borderColor="red">
        <StP>안녕 빨간박스</StP>
      </StBox>
      <StBox borderColor="blue">
        <StP>안녕 파란박스</StP>
      </StBox>
      <StBox borderColor="green">
        <StP>안녕 초록박스</StP>
      </StBox>
    </>
  );
}

export default App;

☞ 이런식으로 조건부 스타일링이 가능해서 용이함

switch와 map으로 리팩토링 하기

import styled from "styled-components";

const StContainer = styled.div`
  display: flex;
`;

const StBox = styled.div`
  width: 100px;
  height: 100px;
  border: 1px solid ${(props) => props.borderColor};
  margin: 20px;
`;

const StP = styled.p`
  color: purple;
`;
//박스의 색
const boxList = ["red", "blue", "green", "black"];

//색을 넣으면 이름을 반환
const getBoxName = (color) => {
  switch (color) {
    case "red":
      return "빨간 박스";
    case "green":
      return "초록박스";
    case "blue":
      return "파란박스";
    default: /// 아무것도 아니면 검은색박스! 
      return "검은박스";
  }
};

function App() {
  return (
    <StContainer>
      {boxList.map((box) => {
        return (
          <StBox borderColor={box}>
            <StP>안녕 {getBoxName(box)}</StP>
          </StBox>
        );
      })}
    </StContainer>
  );
}

export default App;

☞ return 쪽에 {자바스크립트} 넣어주면 활용이 가능함

전역 스타일링

  • 공통적으로 사용 될 스타일이 필요할 때 전역적(globally)으로 스타일을 지정함
//GlobalStyle.jsx
import { createGlobalStyle } from "styled-components";

const GlobalStyle = createGlobalStyle`
body{
  font-family: "나눔고딕", "Arial", sans-serif;
  line-height: 1.5;
}
`;
export default GlobalStyle;

//App.jsx
import GlobalStyle from "./components/GlobalStyle";

return 
<GlobalStyle />

☞ 기존의 컴포넌트처럼 동일하게 사용가능함

CSS RESET

  • HTML 태그들(div, p, h1 등)은 기본적으로 스타일이 적용되어 있어, padding, margin, 폰트 크기 등을 별도로 지정하지 않아도 미리 설정 되어있음

  • 각 브라우저마다 이 기본 스타일이 약간씩 다르게 적용되어 있음.

  • 일관된 스타일을 유지하기 위해 reset.css를 사용할 수 있어서 어느 브라우저에서나 일정한 디자인이 적용됨

//적용할 곳에 상위에 쓸 것
import "./reset.css";

//reset.css
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

React Hook - UseState

  • 가장 기본적인 훅
  • 함수형 컴포넌트 내에서 가변적인 상태를 가짐
const [state, setState] = useState(초기값);
  1. 함수형 업데이트
import { useState } from "react";
import "./App.css";

function App() {
  const [number, setNumber] = useState(0);
  return (
    <>
      <div>Number State : {number}</div>
      <button
        onClick={() => {
          //기존 방식 setNumber(number + 1);
          //함수형 업데이트
          setNumber((currentNumber) => {
            return currentNumber + 1;
          });
        }}
      >
        누르면 UP
      </button>
    </>
  );
}

export default App;

React Hook - UseState _ 일반적인 사용법과 함수형 사용법의 차이

// 일반적인 사용법

import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div>			
      <div>{number}</div> 
      <button
        onClick={() => {
          setNumber(number + 1); 
          setNumber(number + 1); 
          setNumber(number + 1); 
        }}
      >
        누르면 UP
      </button>
    </div>
  );
}

export default App;


//함수형 사용법

import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);
  return (
    <div>
      <div>{number}</div>
      <button
        onClick={() => {
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
          setNumber((previousState) => previousState + 1);
        }}
      >
        누르면 UP
      </button>
    </div>
  );
}

export default App;

☞ 두개 다 버튼을 클릭해보면 일반적인 사용법을 사용했을 때는 아무리 눌러도 1이 된다. 하지만 함수형을 사용하면 3번 반영이 된다.

[부연설명]
1) 일반적인 사용법 방식:

  • 버튼 클릭 시 setNumber를 여러 번 호출해도 React는 명령을 배치(batch)로 처리하여 최종적으로 한 번만 실행함.
  • 3번 setNumber를 호출해도 한 번만 실행되어 최종적으로 1 증가.

2) 함수형 사용법 방식:

  • 버튼 클릭 시 여러 번 setNumber를 호출하면 각 호출을 모아 순차적으로 실행함.
  • 3번 setNumber를 호출하면 각 호출이 순차적으로 1씩 증가하여 최종적으로 3 증가.

React Hook - UseEffect

  • 렌더링 될때 특정한 작업을 수행해야 할때 설정하는 훅
  • 화면에 컴포넌트가 나타났을 때 혹은 사라질 때 실행 하고자 하는 함수를 제어하게 하는 훅
import React, { useEffect } from "react";

const App = () => {
  useEffect(() => {
    console.log("hello useEffect");
  });

  return <div>useEffect</div>;
};

React Hook - UseEffect _ 의존성 배열

  • seEffect의 두 번째 인자로 전달되는 의존성 배열은 해당 값이 변경될 때만 useEffect를 실행한다. → 의존성 배열을 통해 함수의 실행 조건을 제어할 수 있음

1) 배열이 비어 있을때

import { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log(`hello useEffect : ${value}`);
  }, []); //← 여기 []

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

☞ 의존성 배열이 빈 배열인 경우, useEffect는 최초 렌더링 후 한 번만 실행됨

2) 배열에 값이 있을 때

import { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log(`hello useEffect : ${value}`);
  }, [value]); // [value]가 값이 들어 가 있는 것

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

☞ 의존성 배열에 값이 있는 경우, 해당 값이 변경될 때마다 useEffect가 실행됨

React Hook - UseEffect _ 클린업

  • 컴포넌트가 화면에서 사라질 때 실행하고자 하는 함수
import { useEffect, useState } from "react";
import "./App.css";

function App() {
  const [value, setValue] = useState("");
  useEffect(() => {
    console.log(`hello useEffect : ${value}`);

    return ( //여기서 부터 클린업 
      () => {
        console.log("곧 사라집니다.");
      }, // 여기까지 클린업
      [value]
    );
  });

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(event) => {
          setValue(event.target.value);
        }}
      />
    </div>
  );
}

export default App;

☞ useEffect 내부에서 return을 통해 클린 업 함수를 입력하면, 컴포넌트가 사라질 때 해당 함수가 실행됨

React Hook - UseRef

  • DOM 요소에 접근하기 위해 사용하는 React Hook
  • 전에는 getElementById나 querySelector 등을 사용하여 DOM을 선택했지만, useRef를 사용하여 리액트에서도 특정 DOM을 선택할 수 있음

React Hook - UseRef 사용법과 용도

import { useRef } from "react";

  const ref = useRef("초기값");

☞ ref에 저장된 값은 렌더링을 일으키지 않고, 컴포넌트가 다시 렌더링되어도 값이 유지된다.

  • useRef 용도:
  • 저장공간: state와 유사하지만, 값의 변화에 따라 리렌더링이 발생하지 않고 값이 유지된다.
  • DOM 접근: 특정 DOM 요소에 접근할 때 사용된다.

state와 ref 차이점

  • state : 값이 변경 되면 리렌더링 됨
  • ref : 값이 변경 되면 리런더링 안됨 → 값을 계속 유지하고 싶을때 사용함

렌더링 완료 되면 포커스 주기

import { useEffect, useRef } from "react";
import "./App.css";

function App() {
  const idRef = useRef(""); //이렇게 선언하면 아래 input을 특정할 수 있다. 
  useEffect(() => {
    idRef.current.focus(); // 포커스 하게 하는 코드 
  }, []);

  return (
    <>
      <div>
        아이디 : <input type="text" ref={idRef} /> 
      </div>
      <div>
        비밀번호 : <input type="password" />
      </div>
    </>
  );
}

export default App;

ID에 10자가 넘어가면 비밀번호에 포커스하기

import { useEffect, useRef, useState } from "react";
import "./App.css";

function App() {
  const idRef = useRef("");
  const pwRef = useRef("");

  const [id, setId] = useState("");
  useEffect(() => {
    idRef.current.focus();
    //pwRef.current.focus();
  }, []);

  useEffect(() => {
    if (id.length >= 10) {
      pwRef.current.focus();
    }
  }, [id]);
  return (
    <>
      <div>
        아이디 :{" "}
        <input
          type="text"
          ref={idRef}
          value={id} 
          onChange={(e) => {
            setId(e.target.value);
          }}
        />
      </div>
      <div>
        비밀번호 : <input type="password" ref={pwRef} />
      </div>
    </>
  );
}

export default App;

1) ID 감지를 위해 value와 onChange 사용:

value는 입력 필드에 표시되는 값을 나타냄.
onChange는 입력 필드 값 변경 시 호출되며, 변경된 값을 사용하여 ID 상태를 업데이트함.

2) useEffect로 조건부 필터링:
useEffect를 사용하여 특정 조건(ID의 길이가 10이상되면 다른 입력 필드로 이동)을 감지함.

React Hook - UseContext

  • React Context API를 활용하여 전역 상태를 간단하게 관리하기 위해 사용됨
  1. 왜 필요한가?
    Prop drilling 문제:
    데이터를 부모 컴포넌트에서 자식 컴포넌트로 전달할 때, 깊이가 깊어지면 추적이 어려워지고 오류 대응이 어려움.

  2. Context API 필수 개념

  • createContext: Context 생성
  • Consumer: Context 변화 감지
  • Provider: Context 전달 (하위 컴포넌트에 데이터 제공)
  1. 주의사항
  • seContext를 사용할 때, Provider에서 제공한 값이 변경되면 모든 컴포넌트가 리렌더링됨. 따라서 값을 관리할 때 주의가 필요함.
  1. 코드 예시
    [기존의 프롭스]
//GrandFather.jsx
import React from "react";
import Father from "./Father";

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;

  return <Father houseName={houseName} pocketMoney={pocketMoney} />;
}

export default GrandFather;

//Father.jsx
import React from "react";
import Child from "./Child";

function Father({ houseName, pocketMoney }) {
  return <Child houseName={houseName} pocketMoney={pocketMoney} />;
}

export default Father;

//Child.jsx
import React from "react";

function Child({ houseName, pocketMoney }) {
  const stressedWord = {
    color: "red",
    fontWeight: "900",
  };
  return (
    <div>
      나는 이 집안의 막내에요.
      <br />
      할아버지가 우리 집 이름은 <span style={stressedWord}>{houseName}</span>
      라고 하셨어요.
      <br />
      게다가 용돈도 <span style={stressedWord}>{pocketMoney}</span>원만큼이나 주셨답니다.
    </div>
  );
}

export default Child;

☞ 프롭스를 이용해서 부모 컴포넌트에서 자식 컴포넌트로 데이터 전달을 했었는데 깊이가 깊어지면 관리하기에 어려움

[새로운 useContext]

//[새롭게 생성]FamilyContext.js

import { createContext } from "react";
export const FamilyContext = createContext(null);


//GrandFather.jsx
import React from "react";
import Father from "./Father";
import { FamilyContext } from "../context/FamilyContext";

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;

  return (
    <FamilyContext.Provider value={{ houseName, pocketMoney }}>
      <Father />
    </FamilyContext.Provider>
  );
}

export default GrandFather;

//Father.jsx
import React from "react";
import Child from "./Child";

function Father() {
  return <Child />;
}

export default Father;

//Child.jsx
import React, { useContext } from "react";
import { FamilyContext } from "../context/FamilyContext";

function Child({ houseName, pocketMoney }) {
  const stressedWord = {
    color: "red",
    fontWeight: "900",
  };

  const data = useContext(FamilyContext);
  console.log("data", data);

  return (
    <div>
      나는 이 집안의 막내에요.
      <br />
      할아버지가 우리 집 이름은 <span style={stressedWord}>{data.houseName}</span>
      라고 하셨어요.
      <br />
      게다가 용돈도 <span style={stressedWord}>{data.pocketMoney}</span>원만큼이나 주셨답니다.
    </div>
  );
}

export default Child;
  1. 새롭게 컨텍스트를 생성 한다.
export const FamilyContext = createContext(null);
  1. GrandFather.jsx 파일에서 FamilyContext를 import하고, 기존의 houseName과 pocketMoney를 해당 컨텍스트의 Provider로 감싼다.
// GrandFather.jsx
import React from "react";
import Father from "./Father";
import { FamilyContext } from "../context/FamilyContext";

function GrandFather() {
  const houseName = "스파르타";
  const pocketMoney = 10000;

  return (
    <FamilyContext.Provider value={{ houseName, pocketMoney }}>
      <Father />
    </FamilyContext.Provider>
  );
}

export default GrandFather;
  1. Father.jsx에서는 더 이상 props를 받을 필요가 없으므로 해당 부분을 수정하고 Child.jsx에서는 useContext를 사용하여 FamilyContext에서 데이터를 가져온다.
import React from "react";
import Child from "./Child";

function Father() {
  return <Child />;
}

export default Father;

// Father.jsx
import React from "react";
import Child from "./Child";

function Father() {
  return <Child />;
}

export default Father;

오늘의 한줄평 : 와....

profile
귀여운 걸 좋아하고 흥미가 있으면 불타오릅니다💙 최근엔 코딩이 흥미가 많아요🥰

0개의 댓글