Automatic Batching

- Batching은 여러가지 작업을 한번에 처리하는 것
- 원래는 state 업데이트 할 때마다 재렌더링이 되지만
- Automatic Batching은 업데이트를 묶어서 한번에 처리해서 재렌더링을 한다.

 

 

 

 

Transitions

- 긴급한 업데이트와 긴급하지 않은 업데이트를 구분해서 처리하기 위한 개념

 

긴급한 업데이트

- 사용자와 직접적인 인터랙션이 일어나는 경우

- 예) 글자 입력, 버튼 클릭 등

 

긴급하지 않은 업데이트

- 사용자와 직접적인 인터랙션이 일어나지 않는 경우

- 예) 서버로부터 결과를 받아와서 보여주는 경우

 

 

 

 

Suspense

- 리액트로 컴포넌트를 작성하다보면 웹 사이트 규모가 커지고, 컴포넌트 사이즈 역시 커진다.

- 그래서 웹 브라우저에서 이렇게 큰 컴포넌트를 한번에 로딩하면 시간이 오래 걸린다.

- Code Splitting : 컴포넌트의 코드를 여러 조각으로 분리,

- 분리된 조각을 동적 로딩기법으로 반응 속도 높힌다.

- Suspense의 역할은 하위 컴포넌트(children)가 준비되기 전까지 렌더링을 중단하는 것

OtherComponent가 준비되기 전 까지 fallback 속성 안에 LoadingSpinner 컴포넌트를 보여주고, OtherComponent가 준비되면 그때 화면에 보여준다.

 

 

 

 

클라이언트와 서버 렌더링 API 업데이트

리액트 DOM 클라이언트

기존의 방식
새로운 방식 (리액트 버전 18)

 

이제 앞으로 렌더링을 하기 위해서는 createRoot()를 사용하는게 좋다. 기존 버전도 물론 되지만, 기존 버전 사용시 리액트 18에서 사용할 수 있는 기능들을 사용하지 못한다.

 

 

 

리액트 DOM 서버

renderToPipeableStream : NodeJs 환경에서 스트리밍을 위한 함수

renderToReadableStream : Edge runtime 환경(예: Deno, Cloudflare workers)을 위한 함수

 

 

 

 

새로운 Strict 모드 작동 방식

- Strict Mode : 개발모드일 때 잠재적인 버그를 찾게 해주는 모드

- 리액트 18에서는 Strict의 모드의 동작이 변경되었는데, 컴포넌트를 언마운트(unmount) 시켰다가 다시 마운트(mount)

 

 

 

 

새롭게 추가된 훅들

useId()

- 서버와 클라이언트에서 고유한 ID값을 생성하기 위한 훅

- 다만 리스트를 렌더링 할 때, map 함수 내에서 사용하는 엘리먼트의 키로 사용하지 않음

 

useTransitions()

- 위에서 보여줬던 긴급하지 않은 업데이트를 위한 훅

- 기본적으로 일반적인 상태 업데이트는 긴급한 업데이트로 처리되고,

- useTransitions() 이나 startTransitions() 으로 긴급하지 않은 업데이트들을 모아서 처리할 수 있음

 

useDeferredValue()

- 긴급하지 않은 업데이트를 재렌더링하는 걸 연기할 수 있게하는 훅

- 짧은 시간에 한가지 상태의 업데이트가 여러번 발생하게 되면, 최종 상태값만을 업데이트 하면 된다.

 

useSyncExternalStore()

- 외부 저장소를 구독할 수 있게하는 훅

- 외부 저장소를 React 컴포넌트의 State와 연동해서 사용하고 싶을 때 사용.

- 애플리케이션 코드가 아닌 라이브러리에서 사용하기 위한 것이라 입문자들이 사용할 일은 거의 없음

 

useInsertionEffect()

- CSS인 JS 라이브러리를 위한 훅

- 렌더링 과정에서 style 삽입의 성능 문제를 해결하게 해줌

- 위의 useSyncExternalStore() 훅 처럼 일반적인 리액트 개발자들이 사용할 일은 거의 없음

'React' 카테고리의 다른 글

[React] react-router-dom v6  (0) 2024.08.30
[React] styled-component  (0) 2024.08.23
[React] Context  (0) 2024.08.21
[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19

react-router-dom

리액트를 위한 라우팅 라이브러리, 사용자가 원하는 경로로 보내는 과정.

 

 

사용법

 

BrowserRouter는 리액트에서 Routes를 사용할 수 있게 해주는 컴포넌트, Routes와 Route 는 실제로 라우팅 경로를 지정하게 해주는 컴포넌트

 

여기서 path로 경로를 설정하고, element는 경로가 일치할 경우 렌더링할 컴포넌트를 지정한다.

 

 

useNavigate() 훅

페이지 이동을 위해 제공하는 훅

파라미터로 이동할 path를 넣어주면 된다.

 

 

https://reactrouter.com

 

Home v6.26.1 | React Router

 

reactrouter.com

더 자세한 걸 보고 싶다면 해당 사이트에서...

 

 

실습 : App.js 수정

App 컴포넌트는 가장 처음으로 렌더링되는 컴포넌트이다.

import React from "react";
import {
  BrowserRouter,
  Routes,
  Route
} from "react-router-dom";
import styled from "styled-components";
import MainPage from "./component/page/MainPage";
import PostWritePage from "./component/page/PostWritePage";
import PostViewPage from "./component/page/PostViewPage";

const MainTitleText = styled.p`
  font-size: 24px;
  font-weight: bold;
  text-align: center;
`;

function App(props) {
  return (
    <BrowserRouter>
      <MainTitleText>미니 블로그</MainTitleText>
      <Routes>
        <Route index element={<MainPage />} />
        <Route path="post-write" element={<PostWritePage />} />
        <Route path="post/:postId" element={<PostViewPage />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

마지막 Route를 보면 경로에 :postId가 보이는데, 콜론을 사용하는 경우 실제 컴포넌트에서 useParams() 훅을 사용해 아이디 파라미터 값을 가져올 수 있다.

'React' 카테고리의 다른 글

[React] Appendix A. 리액트 버전18  (3) 2024.09.02
[React] styled-component  (0) 2024.08.23
[React] Context  (0) 2024.08.21
[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19

CSS (Cascading Style Sheets)

- 크게 Selector와 Style로 구성되어 있음

h1 { color: green; font-size: 16px; }

 

- 여기서 h1은 Selector(선택자) 이고, color와 font-size는 속성, green과 16px은 값이다. color: green 과 같이 속성과 값은 하나의 Declaration(선언) 이 된다.

 

 

 

 

 

 

 

styled-component

css 문법을 사용하면서 결과물을 스타일링된 컴포넌트 형태로 만들어주는 오픈소스 라이브러리

# npm을 사용하는 경우
npm install --save styled-components

# yarn을 사용하는 경우
yarn add styled-components

다음과 같이 터미널에서 설치를 하면 된다.

 

 

기본적인 사용법

tagged template literal을 사용하여 css 속성이 적용된 리액트 컴포넌트를 만들어준다.

styled 컴포넌트를 사용하는 기본적인 방법은 다음과 같이 백틱스로 둘러싸인 문자열 부분에

css 속성을 넣고, 태그 함수 위치에는 styled.html태그 형태로 사용한다.

이러면 해당 엘리먼트 태그에 css 속성이 적용된 형태의 리액트 컴포넌트가 만들어진다.

 

 

tagged template literal

- literal : 소스코드에 고정된 값

- template literal : 리터럴을 템플릿 형태로 사용하는 자바스크립트 문법, 역따옴표 백틱을 사용

Tagged template literal은 앞에 나와있는 tag function을 호출하여 결과를 리턴한다.
Tagged template literal 예제

 

 

 

styled-components의 props 사용하기

 

여기서 props는 해당 컴포넌트에서 사용된 props를 의미한다. props로 dark가 전달되는 여부에 따라 색상을 다르게 할 수 있음

 

 

스타일 확장

RoundedButton의 styled 옆 부분을 보면 html 태그가 빠져있고, 대신 Button 컴포넌트가 적혀있다.

이는 Button 컴포넌트의 확장된 컴포넌트라는 뜻이다.

 

 

 

 

실습 : styled-components를 사용하여 스타일링 해보기

import styled from "styled-components";

const Wrapper = styled.div`
    padding: 1rem;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    justify-content: flex-start;
    background-color: lightgrey;
`;

const Block = styled.div`
    padding: ${(props) => props.padding};
    border: 1px solid black;
    border-radius: 1rem;
    background-color: ${(props) => props.backgroundColor};
    color: white;
    font-size: 2rem;
    font-weight: bold;
    text-align: center
`;

const blockItems = [
    {
        label: "1",
        padding: "1rem",
        backgroundColor: "red",
    },
    {
        label: "2",
        padding: "3rem",
        backgroundColor: "green",
    },
    {
        label: "3",
        padding: "2rem",
        backgroundColor: "blue",
    },
];

function Blocks(props) {
    return (
        <Wrapper>
            {blockItems.map((blockItem) => {
                return (
                    <Block
                        padding={blockItem.padding}
                        backgroundColor={blockItem.backgroundColor}
                    >
                        {blockItem.label}
                    </Block>
                )
            })}
        </Wrapper>
    );
}

export default Blocks;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import Blocks from './chapter_15/Blocks';

ReactDOM.render(
  <React.StrictMode>
  <Blocks />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

다음과 같이 화면에 나타난다. 여기서 개발자 도구에서 스타일 부분을 수정해본다.

 

 

align-items를 strect로 변경해보았다. 그러자 다음과 같이 화면에 나타난다.

'React' 카테고리의 다른 글

[React] Appendix A. 리액트 버전18  (3) 2024.09.02
[React] react-router-dom v6  (0) 2024.08.30
[React] Context  (0) 2024.08.21
[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19

Context

-  기존 컴포넌트의 props를 통한 데이터 전달방식 대신 컴포넌트 트리를 통해 곧바로 컴포넌트로 전달함

 

 

언제 Context를 사용해야 할까?

- 여러 개의 컴포넌트들이 접근해야 하는 데이터 : 로그인 여부, 로그인 정보, UI 테마, 현재 언어 등...

이 처럼 props를 통해 데이터 전달하는 방식은 실제 데이터를 필요한 컴포넌트가 깊어질 수록 복잡해진다.

 

 

Context를 사용한 방식

 

 

 

Context를 사용하기 전에 고려할 점

- 무조건 Context를 사용하는 것이 좋은게 아니다. 왜냐하면 컴포넌트와 Context가 연동되면 재사용성이 떨어진다.

- 다른 레벨의 많은 컴포넌트가 데이터를 필요로 하는 경우가 아니라면, 기존 사용방식인 props 를 사용하는게 좋다.

 

 

 

Context API

React.createContext() : context를 생성

const MyContext = React.createContext(기본값);

- 만약 상위 레벨에 매칭되는 Provider가 없다면 기본값이 사용 됨!

- 기본값으로 undefined를 넣으면 기본값이 사용되지 않음.

 

 

Context.Provider : 제공자라는 뜻, 데이터를 제공해주는 컴포넌트

<MyContext.Provider value={/* some value */}>

- value 는 Provider 하위에 있는 컴포넌트들에게 전달된다.

- Provider value에서 주의해야 할 사항

Provider 컴포넌트가 재렌더링될 때마다 모든 하위 consumer 컴포넌트가 재렌더링 됨.

 

 

이와 같이 state를 사용하여 불필요한 재렌더링을 막는다.

 

 

 

Class.contextType

- Provider 하위에 있는 클래스 컴포넌트에서 context 데이터에 접근하기 위해 사용함

- 지금은 사용하지 않기 때문에 이런게 있다라는 것만 알아주기

 

 

 

Context.Consumer

- context의 데이터를 구독

<MyContext.Consumer>
	{value => /* 컨텍스트의 값에 따라서 컴포넌트들을 렌더링 */}
</MyContext.Consumer>

 

 

function as a child

- 컴포넌트의 자식으로 함수를 사용하는 방법

// children이라는 prop을 직접 선언하는 방식
<Profile children={name => <p>이름: {name}</p>} />

// Profile 컴포넌트로 감싸서 children으로 만드는 방식
<Profile>{name => <p>이름: {name}</p>}</Profile>

 

 

Context.displayName

- Context 객체는 displayName이라는 문자열 속성을 가진다.

- 크롬의 React 개발자 도구에서는 Context의 Provider나 Consumer를 표시할 때 이 DisplayName을 함께 표시해준다.

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

// 개발자 도구에 "MyDisplayName.Provider"로 표시됨
<MyContext.Provider>

// 개발자 도구에 "MyDisplayName.Consumer"로 표시됨
<MyContext.Consumer>

 

 

 

 

여러개의 Context 사용하기

다음과 같이 Provider를 중첩해서 사용 가능

 

 

 

useContext() Hook

- 함수 컴포넌트에서 Context를 보다 쉽게  사용할 수 있게 함

사용시 파라미터에 Context 객체를 넣어줘야 함

 

 

 

 

 

실습 : Context를 사용하여 테마 변경 기능 만들기

import React from "react";

const ThemeContext = React.createContext();
ThemeContext.displayName = "ThemeContext";

export default ThemeContext;

ThemeContext라는 Context를 만든다. 기본값은 없다.

 

 

 

import { useContext, userContext } from "react";
import ThemeContext from "./ThemeContext";

function MainContent(props) {
    const { theme, toggleTheme } = useContext(ThemeContext);

    return (
        <div
            style={{
                width: "100vw",
                height: "100vh",
                padding: "1.5rem",
                backgroundColor: theme == "light" ? "white" : "black",
                color: theme == "light" ? "black" : "white",
            }}
        >
            <p>안녕하세요, 테마 변경이 가능한 웹사이트 입니다.</p>
            <button onClick={toggleTheme}>테마 변경</button>
        </div>
    );
}

export default MainContent;

MainContent 컴포넌트는 ThemeContext로 부터 데이터를 가져와 해당 테마대로 렌더링을 하는 역할을 한다.

또한 테마 변경 버튼을 누를 경우 ThemeContext로 부터 받은 toggleTheme 함수를 호출하여 ThemeContext의 값을 변경하는 역할을 한다.

 

 

import { useState, useCallback } from "react";
import ThemeContext from "./ThemeContext";
import MainContent from "./MainContent";

function DarkOrLight(props) {
    const [theme, setTheme] = useState("light");

    const toggleTheme = useCallback(() => {
        if (theme == "light") {
            setTheme("dark");
        } else if (theme == "dark") {
            setTheme("light");
        }
    }, [theme]);

    return (
        <ThemeContext.Provider value={{ theme, toggleTheme }}>
            <MainContent />
        </ThemeContext.Provider>
    );
}

export default DarkOrLight;

DarkOrLight 컴포넌트에서 다음과 같이 작성한다.

Provider로 하위 컴포넌트에서 theme과 toggleTheme을 사용 가능하도록 만든다.

 

 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import DarkOrLight from './chapter_14/DarkOrLight';

ReactDOM.render(
  <React.StrictMode>
  <DarkOrLight />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

실행 결과, 테마 변경 버튼을 누르면 테마가 변경되는 것을 확인할 수 있다.

'React' 카테고리의 다른 글

[React] react-router-dom v6  (0) 2024.08.30
[React] styled-component  (0) 2024.08.23
[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19
[React] Lifting State Up  (0) 2024.08.19

Composition

- 영어 뜻 : 구성
- 여러개의 컴포넌트를 합쳐서 새로운 컴포넌트를 만드는 것
- 그래서 구성 보다는 합성이라는 단어가 어울림

 

조합 방법

Containment

- 담다, 포함하다
- 하위 컴포넌트를 포함하는 형태의 합성 방법
- SideBar나 Dialog 같은 Box 형태의 컴포넌트는 자신의 하위 컴포넌트를 미리 알 수 없다.
- children이라는 prop을 사용해서 조합

children 이라는 prop은 리액트에서 기본적으로 제공한다. 이것을 사용하면 해당 컴포넌트의 하위 컴포넌트가 모두 children으로 들어오게 된다.
이전에 createElement를 배웠을 때, ...children이 하위 컴포넌트라는 것을 배웠었다.
FancyBorder 컴포넌트 안에 있는 모든 JSX 태그는 children으로 전달됨!!

 

 

 

여러개의 children 집합이 필요한 경우 : 별도로 props를 정해서 각각 원하는 컴포넌트를 넣어주면 된다.

다음은 화면을 왼쪽과 오른쪽으로 분할해서 보여주는 역할을 하는 SplitPane 컴포넌트이다. 

 

App 이라는 컴포넌트에서는 SplitPane 컴포넌트를 사용하고 있는데, 여기서 left, right라는 두개의 props를 정의해서 그 안에 각각 다른 컴포넌트를 넣어주고 있다.

 

SplitPane 에서는 이 left, right 라는 props를 이용해서 각각 왼쪽과 오른쪽 화면을 분리해서 렌더링하여 보여준다.

 

이 처럼 여러개의 children 집합이 필요한 경우에는 별도의 props를 정의해서 사용하면 된다.

 

 

 

Specialization

- 전문화, 특수화

- 예) Welcome Dialog는 Dialog의 특별한 케이스다.

- 범용적인 개념을 구별이 되게 구체화 하는 것

- 기존의 객체지향 언어에서는 상속을 사용하여 Specialization을 구현한다. 그러나! BUT!

- React에서는 합성(Composition)을 사용하여 Specialization을 구현한다.

Dialog라는 범용적인 컴포넌트와 WelcomeDialog라는 특별한 컴포넌트

제목과 메세지를 WelcomeDialog에서 정의해서 제목과 메세지를 어떻게 사용하냐에 따라 인삿말 다이어로그가 되거나 경고 다이어로그가 된다.

 

 

 

Containment 와 Specialization을 같이 사용하기

children prop 을 통해 Dialog 하위 태그들을 Containment 방식 사용해서 불러 올 수 있으며, title 과 message prop으로 제목과 메세지 정의해서 특수화하여 사용하면 Specialization 역시 구현할 수 있다.

 

 

 

 

Inheritance

- Composition과 대비되는 개념

- 상속

- 다른 컴포넌트로부터 상속을 받아서 새로운 컴포넌트를 만드는 것

- 그러나 추천할 만한 사용은 아니다. 그래서 Composition 사용을 권장한다.

 

 

 

실습 : Card 컴포넌트 만들기

function Card(props) {
    const { title, backgroundColor, children } = props;

    return (
        <div
            style={{
                margin: 8,
                padding: 8,
                borderRadius: 8,
                boxShadow: "0px 0px 4px grey",
                backgroundColor: backgroundColor || "white",
            }}
        >
            {title && <h1>{title}</h1>}
            {children}
        </div>
    );
}

export default Card;

Card 컴포넌트는 하위 컴포넌트를 감싸서 카드 형태로 보여준다. Containment 와 Specialization 두가지 합성 방법을 모두 사용. children 부분이 Containment 부분이고, title과 backgroundColor 부분이 Specialization 부분이다.

 

 

import Card from "./Card";

function ProfileCard(props) {
    return (
        <Card title="Manse Kim" backgroundColor="#4ea04e">
            <p>안녕하세요, 만세입니다.</p>
            <p>리액트 공부 재미있네요.</p>
        </Card>
    );
}

export default ProfileCard;

ProfileCard 컴포넌트에서 Card 컴포넌트를 사용하여 타이틀에 이름을 넣고, 배경색깔 역시 넣는다.

children 으로는 간단한 자기소개 글을 넣었다.

 

 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import ProfileCard from './chapter_13/ProfileCard';

ReactDOM.render(
  <React.StrictMode>
  <ProfileCard />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

인덱스에서 렌더링하고 터미널에서 실행하면

 

 

제목과 배경색, 그리고 children이 합성되어 하나의 카드로 보여주는 것을 확인할 수 있다.

'React' 카테고리의 다른 글

[React] styled-component  (0) 2024.08.23
[React] Context  (0) 2024.08.21
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19
[React] Lifting State Up  (0) 2024.08.19
[React] Form과 Controlled Component  (0) 2024.08.18

지난 포스팅에서 Shared State를 배우기 위해 섭씨온도, 화씨온도 변환하는 것을 배웠는데, 직접 실습을 통해 확인해볼 것

 

 

const scaleNames = {
    c: "섭씨",
    f: "화씨",
};

function TemperatureInput(props) {
    const handleChange = (event) => {
        props.onTemperatureChange(event.target.value);
    };

    return (
        <fieldset>
            <legend>
                온도를 입력해주세요(단위:{scaleNames[props.scale]}):
            </legend>
            <input value={props.temperature} onChange={handleChange} />
        </fieldset>
    );
}

export default TemperatureInput;

 

일단 하위 컴포넌트인 TemperatureInput 컴포넌트 작성

 

 

import React, { useState } from "react";
import TemperatureInput from "./TemperatureInput";

function BoilingVerdict(props) {
    if (props.celsius >= 100) {
        return <p>물이 끓습니다.</p>;
    }
    return <p>물이 끓지 않습니다.</p>;
}

function toCelsius(fahrenheit) {
    return ((fahrenheit - 32) * 5) / 9;
}

function toFahrenheit(celsius) {
    return (celsius * 9) / 5 + 32;
}

function tryConvert(temperature, convert) {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
        return "";
    }
    const output = convert(input);
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}

function Calculator(props) {
    const [temperature, setTemperature] = useState("");
    const [scale, setScale] = useState("c");

    const handleCelsiusChange = (temperature) => {
        setTemperature(temperature);
        setScale("c");
    };

    const handleFahrenheitChange = (temperature) => {
        setTemperature(temperature);
        setScale("f");
    };

    const celsius = 
        scale === "f" ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = 
        scale === "c" ? tryConvert(temperature, toFahrenheit) : temperature;

    return(
        <div>
            <TemperatureInput 
                scale="c"
                temperature={celsius}
                onTemperatureChange={handleCelsiusChange}
            />
            <TemperatureInput 
                scale="f"
                temperature={fahrenheit}
                onTemperatureChange={handleFahrenheitChange}
            />
            <BoilingVerdict celsius={parseFloat(celsius)} />
        </div>
    );
}

export default Calculator;

 

상위 컴포넌트 Calculator에서 하위 컴포넌트 TemperatureInput 으로 정보를 전송하여 입력 후 온도 변환값이 나타나게 한다.

 

 

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import Calculator from './chapter_12/Calculator';

ReactDOM.render(
  <React.StrictMode>
  <Calculator />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

터미널에서 npm start로 실행해본다.

 

 

 

섭씨 온도에 99를 입력하자 화씨 온도 부분에서 변환된 온도가 나타나게 된다.
섭씨 110도에서 물이 끓는다.
마찬가지로 화씨에서 온도를 입력하면 섭씨에서 변환된 온도값을 나타낸다.

'React' 카테고리의 다른 글

[React] Context  (0) 2024.08.21
[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] Lifting State Up  (0) 2024.08.19
[React] Form과 Controlled Component  (0) 2024.08.18
[React] List 와 Key  (0) 2024.08.13

Lifting State Up

- 하위 컴포넌트의 State를 공통 상위 컴포넌트로 올림

 

 

Shared State

- 리액트로 개발시, 하나의 데이터를 여러대의 컴포넌트에서 표현해야 할 때가 있음.
- 이럴 경우 데이터를 각각 보관하는 게 아니라, 가장 가까운 공통된 부모 컴포넌트의 state를 공유해서 사용함
- State에 있는 데이터를 여러개의 하위 컴포넌트에서 공통적으로 사용하는 경우

 

이렇듯 하위 컴포넌트가 공통된 부모 컴포넌트의 state를 공유하여 사용하는 것

 

 

 

 

사용법 : 썹씨온도와 화씨온도 나타내기

하위 컴포넌트에서 State 공유하기

물의 끓음 여부를 알려주는 컴포넌트
부모 컴포넌트, State로 온도 값을 가지고 있고, 사용자가 온도값을 변경할 때마다 handleChange() 함수 호출됨

BoilingVerdict 컴포넌트에 celsius 라는 prop으로 전달됨

 

 

 

입력 컴포넌트 추출하기

온도를 입력받기 위한 TemperatureInput 컴포넌트

위의 Calculator 컴포넌트에서 온도를 입력 받는 부분을 추출하여 별도의 컴포넌트로 만들었다. 추가적으로 props로 단위를 나타내는 scale을 추가하여 온도의 단위를 썹시, 화씨로 나타내도록 함

 

 

 

변경한 Calculater 컴포넌트

하나는 섭씨, 하나는 화씨 온도를 입력받게 한다. 그러나 문제점은 사용자가 입력한 온도값이 TemperatureInput 컴포넌트의 State에 저장되기 때문에 썹씨온도와 화씨온도값을 따로 입력받으면 두개의 값이 다를 수 있다.

 

이를 해결하기 위해 값을 동기화 시켜줘야 한다.

 

 

온도 변환 함수 작성하기

섭씨를 화씨로, 화씨를 섭씨로 변환하는 함수
위의 변환함수를 호출하는 함수
온도값과 변환하는 함수를 파라미터로 넣어줌

 

 

 

Shared State 적용하기

먼저 TemperatureInput 컴포넌트에서 온도값을 가져오는 부분을 다음과 같이 수정한다. 이렇게 하면 온도값을 컴포넌트 State에서가 아닌 props에서 가져오게 된다.

 

 

handleChange() 함수 역시 다음과 같이 수정, 변경된 온도값이 상위 컴포넌트로 전달된다.

 

 

최종적으로 완성된 TemperatureInput 컴포넌트, State는 제거되었고, 오로지 상위 컴포넌트에서 전달받은 값만을 사용

 

 

 

Calculator 컴포넌트 변경하기

temperature와 scale에 온도값과 단위값을 각각 저장

TemperatureInput 컴포넌트 사용 부분에서는 각 단위로 변환된 온도값과 단위를 props로 넣어주었고, 값이 변경되었을 때 업데이트 하기 위한 함수를 onTemperatureChange에 넣어줌.  즉 섭씨온도가 변경되면 단위가 'c'로 변경되고 화씨온도가 변경되면 단위라 'f'로 변경된다.

 

 

 

최종적으로 완성된 구조

상위 컴포넌트 Calculator에서 온도값과 단위값을 각각 가지고 있으면, 두개의 하위 컴포넌트는 각각 섭씨와 화씨로 변환된 온도값과 단위, 그리고 온도를 업데이트하기 위한 onTemperatureChange() 함수를 props로 가지고 있다.

'React' 카테고리의 다른 글

[React] Composition 방법과 Inheritance  (0) 2024.08.20
[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19
[React] Form과 Controlled Component  (0) 2024.08.18
[React] List 와 Key  (0) 2024.08.13
[React] Conditional Rendering  (0) 2024.08.12

Form

- 양식
- 사용자로부터 입력을 받기 위해 사용

HTML 형식의 Form



 

Controlled Component

- 값이 리액트의 통제를 받는 Input Form Element
- 사용자의 입력을 직접적으로 제어할 수 있음!

 

HTML에서의 Form과 Controlled Component의 차이
이런식으로 사용자가 값을 입력시, 입력한 값을 직접적으로 제어할 수 있음
사용자가 입력한 값을 대문자로 변경

 

 

 

textarea 태그

- 여러 줄에 걸쳐 긴 텍스트를 입력받기 위한 HTML 태그

 

 

Select 태그

- Drop-down 목록을 보여주기 위한 HTML 태그

각각 input, textarea, select 태그



 

File Input 태그

- 디바이스의 저장 장치로부터 하나 또는 여러개의 파일을 선택할 수 있게 해주는 HTML 태그
- 읽기 전용이므로 Uncontrolled Component, 리액트의 통제를 받지 않는다.


 

Multiple Inputs

- 하나의 컴포넌트에서 여러개의 입력을 다루려면 어떻게 해야 할까?
- 여러개의 state를 선언하여 각각의 입력에 대해 사용

각각 set을 달리하여 값을 집어넣으면 된다.



 

Input Null Value

- 앞에서 배운 것 처럼 제어 컴포넌트에 value prop을 정해진 값으로 넣으면 코드 수정하지 않은 한 입력값 변경 불가능
- value prop을 넣되 자유롭게 입력하게 만들고 싶으면 값에 undifined 혹은 null을 넣으면 된다.

자유롭게 입력하고 싶을 때, null을 값에 넣음

 

 

 

 

실습 : 사용자 정보 입력받기

사용자 이름 입력 받기

 

import React, { useState } from "react";

function SignUp(props) {
    const [name, setName] = useState("");

    const handleChangeName = (event) => {
        setName(event.target.value);
    };

    const handleSubmit = (event) => {
        alert(`이름: ${name}`);
        event.preventDefault();
    };

    return (
        <form onSubmit={handleSubmit}>
            <label>
                이름:
                <input type="text" value={name} onChange={handleChangeName} />
            </label>
            <button type="submit">제출</button>
        </form>
    );
}

export default SignUp;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import SignUp from './chapter_11/SignUp';

ReactDOM.render(
  <React.StrictMode>
  <SignUp />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

 

이름을 입력 받으면 알림창에 사용자의 이름이 뜨도록 한다.

 

 

 

성별 정보 추가

import React, { useState } from "react";

function SignUp(props) {
    const [name, setName] = useState("");
    const [gender, setGender] = useState("남자");

    const handleChangeName = (event) => {
        setName(event.target.value);
    };

    const handelChangeGender = (event) => {
        setGender(event.target.value);
    };

    const handleSubmit = (event) => {
        alert(`이름: ${name}, 성별: ${gender}`);
        event.preventDefault();
    };

    return (
        <form onSubmit={handleSubmit}>
            <label>
                이름:
                <input type="text" value={name} onChange={handleChangeName} />
            </label>
            <br />
            <label>
                성별:
                <select value={gender} onChange={handelChangeGender}>
                    <option value="남자">남자</option>
                    <option value="여자">여자</option>
                </select>
            </label>
            <button type="submit">제출</button>
        </form>
    );
}

export default SignUp;

select 태그를 통해 handleChangeGender 라는 이벤트 핸들러로 성별 정보를 변경하게끔 만들었다. gender 라는 state 가 추가되었다.

 

 

 

 

입력 결과 다음과 같이 알림창이 뜬다.

'React' 카테고리의 다른 글

[React] 실습 : 섭씨온도, 화씨온도 표시하기  (0) 2024.08.19
[React] Lifting State Up  (0) 2024.08.19
[React] List 와 Key  (0) 2024.08.13
[React] Conditional Rendering  (0) 2024.08.12
[React] Event  (0) 2024.08.08

List

- Array 배열
- JavaScript의 변수나 객체들을 하나의 변수로 묶어 놓은 것

 

 

Key

- 각 객체나 아이템을 구분할 수 있는 고유한 값
- Key의 값은 같은 List에 있는 Elements 사이에서만 고유한 값이면 된다.

다음과 같이 같은 Element 사이에서의 고유한 값이면 된다.

 

 

 

key로 값을 사용하는 경우. 이러면 중복값이 있을 경우 키값도 중복되기 때문에, 고유해야하는 key의 조건에 충족되지 않는다. (사용 x)
key로 id를 사용하는 경우, id값은 고유한 값이기 때문에 key 값의 조건에 충족한다. (권장)
key로 index를 사용하는 경우, index도 고유한 값이지만 아이템의 순서가 바뀔 경우 문제가 되기 때문에 권장X



 

여러개의 Component 렌더링 하기

- map() : 배열에 들어있는 각 변수에 처리를 한 뒤 리턴
- const doubled = numbers.map((number) => number * 2);
- 위는 numbers 배열의 각 변수를 2배로 한 뒤 리턴

 

numbers 배열에 잇는 값들로 map() 함수를 이용해 listItems 라는 컴포넌트 배열을 만든다. 그리고 ul 태그안에 넣고 렌더링한다.

 

 

 

 

그럼 다음과 같은 결과가 리턴될 것이다.

 

 

주의할 점!

- map() 함수 안에 있는 Elements는 꼭 key가 필요하다.

map() 함수를 이용할 때, 키값을 설정하지 않으면 다음과 같은 오류가 뜬다.

 

 

 

 

 

실습 : 출석부 출력하기

import React from "react";

const students = [
    {
        name: "Inje",
    },
    {
        name: "Steve",
    },
    {
        name: "Bill",
    },
    {
        name: "Jeff",
    },
];

// map() 함수 사용
function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student) => {
                return <li>{student.name}</li>
            })}
        </ul>
    );
}

export default AttendanceBook;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import AttendanceBook from './chapter_10/AttendanceBook';

ReactDOM.render(
  <React.StrictMode>
  <AttendanceBook />
  </React.StrictMode>,
  document.getElementById('root')
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

다음과 같이 map() 함수를 활용하여 student 배열에 있는 학생 이름들을 나열시켰다. 그런데, 문제는 map() 함수 사용 시, Key 값을 사용하지 않았기에 에러가 문구가 뜨는 것을 확인할 수 있다.

 

 

 

import React from "react";

const students = [
    {
        id: 1,
        name: "Inje",
    },
    {
        id: 2,
        name: "Steve",
    },
    {
        id: 3,
        name: "Bill",
    },
    {
        id: 4,
        name: "Jeff",
    },
];

// map() 함수 사용
function AttendanceBook(props) {
    return (
        <ul>
            {students.map((student) => {
                return <li key={student.id}>{student.name}</li>
            })}
        </ul>
    );
}

export default AttendanceBook;

id 값을 설정 후, map() 함수에서 key 값으로 학생들의 고유 데이터를 가져온 뒤, 이름을 출력하였다. 이러면 콘솔창에서 에러가 뜨지 않는다.

 

 

        <ul>
            {students.map((student) => {
                return <li key={`student-id-${student.id}`}>{student.name}</li>
            })}
        </ul>

포멧팅 된 문자열을 키로 사용하는 것 역시 가능하고

 

 

        <ul>
            {students.map((student, index) => {
                return <li key={index}>{student.name}</li>
            })}
        </ul>

index를 사용하는 방법도 있으나 위에서 말한 것 처럼 권장하지 않는다.

'React' 카테고리의 다른 글

[React] Lifting State Up  (0) 2024.08.19
[React] Form과 Controlled Component  (0) 2024.08.18
[React] Conditional Rendering  (0) 2024.08.12
[React] Event  (0) 2024.08.08
[React] Hooks 실습  (0) 2024.08.07

Conditional Rendering

- 조건에 따른 렌더링(조건부 렌더링)
- 어떠한 조건에 따라서 렌더링이 달라지는 것
- 예) Ture이면 버튼을 보여주고 False이면 버튼을 가린다.

만약 로그인됐으면 유저를 환영한다는 컴포넌트를 반환하고, 그렇지 않으면 게스트를 환영한다는 컴포넌트를 반환

 

 

Truthy, Falsy

- true는 아닌데 true로 여겨지는 값
- false는 아니지만 false로 여겨지는 값

truthy와 falsy의 예



 

Element Variables

- 렌더링 해야하는 컴포넌트를 변수처럼 사용해야 할 때

button이라는 변수에 컴포넌트를 집어넣어서 리턴하는 방식

 


Inline Condition

- 코드를 별도로 분리하지 않고, 조건문을 코드 안에 집어넣음

 

 

Inline If

- If문의 경우 && 연산자를 사용
- true && expression 첫번째 조건문이 true라서 두번째 expression 문도 평가함
- false && expression 첫번째 조건문이 false면 두번째 expression 문은 굳이 평가하지 않음
- 그래서 위의 결과값은 expression이고 아래의 결과값은 false

읽지않은 메세지 갯수가 0보다 클 경우 해당 태그를 리턴하지 않고, 0보다 작을 경우 해당 태그 리턴



 

Inline If-Else

- ? 연산자를 사용
- 조건에 따라 각기 다른 Element를 렌더링 하고 싶을 때 사용
- condition ? true : false (삼향연산자)

삼향연산자, 로그인되었다면 '로그인'을 반환하고 아니라면 '로그인하지 않은'을 반환
이 역시, 로그인 여부에 따라 반환하는 컴포넌트를 다르게 함



 

 

Component 렌더링 막기

- null을 리턴하면 렌더링 되지 않음

경고 메세지가 없다면 null을 반환하고 경고 메세지를 나타나게 하지 않음
showWarning의 값에 따라 경고 메세지를 감추거나 보이게 할 수 있음

 

 

 

 

 

실습 : 로그인 여부를 나타내는 툴바 만들기

import React from "react";

const styles = {
    wrapper: {
        padding: 16,
        display: "flex",
        flexDirection: "row",
        borderBottom: "1px solid grey",
    },
    greeting: {
        marginright: 8,
    },
};

function Toolbar(props) {
    const { isLoggedIn, onClickLogin, onClickLogout } = props;
    // 로그인 여부에 따라 환영 여부를 보여주거나 감추고
    // 로그인 여부에 따라 로그인, 로그아웃 버튼을 보여준다
    return (
        <div style={styles.wrapper}>
            {isLoggedIn && <span style={styles.greeting}>환영합니다!</span>}

            {isLoggedIn ? (
                <button onClick={onClickLogout}>로그아웃</button>
            ) : (
                <button onClick={onClickLogin}>로그인</button>
            )}
        </div>
    );
}

export default Toolbar

 

툴바 컴포넌트를 만든다. 로그인 여부에 따라 환영 인사를 보여주고, 로그인, 로그아웃 버튼을 보여주는 방식이다.

 

 

import React, { useState } from "react";
import Toolbar from "./Toolbar";

// useState 훅을 이용해 사용자의 로그인여부를 자체적으로 관리
function LandingPage(props) {
    const [isLoggedIn, setIsLoggedIn] = useState(false);

    const onClickLogin = () => {
        setIsLoggedIn(true);
    };

    const onClickLogout = () => {
        setIsLoggedIn(false);
    };

    // Toolbar 컴포넌트에 전달
    return (
        <div>
            <Toolbar
                isLoggedIn={isLoggedIn}
                onClickLogin={onClickLogin}
                onClickLogout={onClickLogout}
            />
            <div style={{ padding: 16 }}>리액트 공부 같이해요!</div>
        </div>
    );
}

export default LandingPage;

useState 훅을 이용해 사용자의 로그인 여부를 저장한다. 역시 처음에는 false가 초기값이다.  누르면 로그인 여부를 바꿔주는 onClickLogin과 onClickLogout을 Toolbar 컴포넌트에 전달하고, 로그인 여부가 저장되어 있는 isLoggedIn 역시 전달한다. 전달 받은 값들을 위의 Toolbar 컴포넌트에서 활용하면 된다.

 

 

 

 

로그인 버튼을 누르면 환영 메세지가 뜨고, 로그아웃 버튼을 반환한다. 로그아웃 버튼을 누르면 원래대로 돌아가게 된다.

'React' 카테고리의 다른 글

[React] Form과 Controlled Component  (0) 2024.08.18
[React] List 와 Key  (0) 2024.08.13
[React] Event  (0) 2024.08.08
[React] Hooks 실습  (0) 2024.08.07
[React] Hooks  (0) 2024.08.06

+ Recent posts