React

지니씨·2023년 7월 18일
0

프론트엔드

목록 보기
76/85
  • React 컴포넌트는 상태(state) 또는 속성(props)이 변경되면 전체 컴포넌트를 다시 렌더링 함
  • 핵심 패턴 : https://www.heropy.dev/p/OidQSe

Input

controlled input

  • 제어되는 input을 렌더링하려면, value (또는 체크박스와 라디오에는 checked) prop 을 전달
  • 제어되는 컴포넌트는 항상 null이나 undefined가 아닌 문자열 value를 받아야 함
  • 일반적으로 state 변수를 선언하여 사용
  • value와 onChange를 사용하여 상태를 완전히 외부에서 관리
  • onChange 이벤트 핸들러 내에서 해당 state 변수를 동기적으로 업데이트해야 함
import React, { useState } from 'react';

const ControlledInput = () => {
  const [value, setValue] = useState('');

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setValue(event.target.value);
  };

  return (
    <input type="text" value={value} onChange={handleChange} />
  );
};

uncontrolled input

  • 제어되지 않는 input들에만 적용 가능한 속성
    • defaultChecked: 불리언 타입, type="checkbox"type="radio" input에 대한 초기값 지정
    • defaultValue: 문자열 타입, 텍스트 input에 대한 초기값 을 지정
  • defaultValue를 통해 초기값만 설정, 이후 ref를 통해 값에 접근하거나 제어
import React, { useRef } from 'react';

const UncontrolledInput = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  const handleClick = () => {
    if (inputRef.current) {
      alert(inputRef.current.value);
    }
  };

  return (
    <>
      <input type="text" defaultValue="Initial Value" ref={inputRef} />
      <button onClick={handleClick}>Show Value</button>
    </>
  );
};

데이터 처리

  • React에서는 useState와 onChange 핸들러를 사용
import React, { useState } from 'react';

const InputComponent = () => {
  const [inputValue, setInputValue] = useState('');

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };

  return <input value={inputValue} onChange={handleChange} />;
};

export default InputComponent;
// 부모 컴포넌트
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';

const ParentComponent = () => {
  const [parentValue, setParentValue] = useState('');

  return (
    <div>
      <ChildComponent value={parentValue} onChange={setParentValue} />
      <p>Parent Value: {parentValue}</p>
    </div>
  );
};

export default ParentComponent;
// 자식 컴포넌트
import React from 'react';

interface ChildComponentProps {
  value: string;
  onChange: (newValue: string) => void;
}

const ChildComponent: React.FC<ChildComponentProps> = ({ value, onChange }) => {
  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    onChange(event.target.value);
  };

  return <input value={value} onChange={handleInputChange} />;
};

export default ChildComponent;
  • 양방향 데이터 바이딩 (like Vue 의 v-model)
// 부모 컴포넌트
<template>
  <ChildComponent v-model="parentValue" />
</template>

<script>
export default {
  data() {
    return {
      parentValue: '',
    };
  },
};
</script>
// 자식 컴포넌트
<template>
  <input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>

<script>
export default {
  props: ['modelValue'],
};
</script>

렌더링

  • &&: 값이 있을 경우 추가 렌더링을 위한 조건부 렌더링
// 존재하는 children만 표시
return ( children && (
  <div>
    {children}
  </div> ));
  • ll: 기본값을 제공하거나 대체 요소를 렌더링하려는 경우
// children이 없을 경우 대체 요소를 표시
return ( children || (
  <div>
    <Component></Component>
  </div> ));

Router

React Router

Library mode vs Framework mode

Next.js Router

컴포넌트 정의

  • React 컴포넌트는 마크업으로 뿌릴 수 있는 JavaScript 함수

  • React 컴포넌트의 이름은 항상 대문자로 시작, HTML 태그는 소문자로 시작

  • 컴포넌트 정의 방법 중 2안 선택

    • Component를 extending하는 클래스 컴포넌트 (가이드상 지양)
    • JSX를 리턴하는 함수형 컴포넌트
  • 컴포넌트 구현

    • 기본
    const ComponentName = (props: ComponentProps) => {
      const {
        속성명 = 기본값,
        className = '',
        children,
        ...reset
      } = props;
      ...
      return ( <ComponentName {...reset}> ... </ComponentName> )
    }
    export default ComponentName;
    
    • 컴포넌트의 DOM 참조가 필요한 경우
    const ComponentName = forwardRef((props: ComponentProps, ref) => {
      const {
        속성명 = 기본값,
        ...reset
      } = props;
      ...
      return ( <ComponentName ref={ref} {...reset}> ... </ComponentName> )
    })
    export default ComponentName;

Hooks

  • 커스텀 훅
    • 동일한 상태나 로직이 여러 컴포넌트에서 반복될 때, 커스텀 훅을 만들어 중복을 제거하고 코드 재사용성을 높임
    • 컴포넌트에서 비즈니스 로직을 분리해 가독성과 유지보수를 향상

useTransition

useDeferredValue

  • https://ko.react.dev/reference/react/useDeferredValue#deferring-re-rendering-for-a-part-of-the-ui
  • 디바운싱은 타이핑을 멈출 때까지(e.g. 1초 동안) 기다렸다가 목록 업데이트, 스로틀링은 가끔씩(e.g. 최대 1초에 한 번) 목록 업데이트
  • debounce의 대체제는 아님. 빈번하게 바뀌는 상태 값에 영향을 받는 네트워크 요청에 대한 최적화를 하고 싶으면 debounce 사용. 빈번히 바뀌는 상태 값에 영향을 받는 굉장히 비싼 렌더링을 최적화 할때 useDefferedValue 사용.

(useId)

메모이제이션

  • 값을 계산하거나 함수 결과를 반복적으로 사용할 때, 값이나 함수를 캐시해 불필요한 재계산을 방지하는 최적화 기법
  • 함수나 값이 비교적 가변운 경우 useCallback, useMemo로 인한 성능 개선은 미미하거나 오히려 성능 저하를 유발할 수 있음
  • 자주 변경되거나 컴포넌트가 빈번히 렌더링되는 경우 도움이 됨

값 캐싱 (useMemo)

  • Vue의 computed -> React의 useMemo
  • 특정 값이 변경될 때만 계산, 데이터 캐싱, 불필요한 재계산 방지
  • useMemo는 첫 번째 렌더링에서의 속도 차이가 없음, 업데이트 시 불필요한 작업을 건너뛰는 데만 도움이 됨
  • console.time('test');... console.timeEnd('test');활용해 코드 소요시간 측정, 전체 기록 시간이 1ms이상이라면 해당 계산 메모이제이션하길 권장
  • useMemo는 컴포넌트의 렌더링을 트리거하지 않음, 단지 값이 변경되었을 때 재계산을 수행하여 반환하는 역할
  • 코드 소요 시간이 1ms 이상이라면 메모이제이션 고려
  • useMemo를 사용하지 않으면 매번 새로 계산하지만 useMemo를 사용하는 것도 성능에 영향을 주기 때문에 복잡한 연산을 캐싱할 때만 사용

메소드 캐싱 (useCallback)

  • Vue는 기본적으로 컴포넌트 메서드가 캐싱되어 있음
  • React 에서는 명시적으로 useCallback을 사용해 명시적으로 캐싱
  • 컴포넌트가 재 렌더링될 때 동일한 함수 재사용하도록 함 (성능 최적화)
  • 특정 상황에서는 함수 재생성과 그로 인한 자식 컴포넌트의 불필요한 재렌더링도 방지할 수 있음
  • 꼭 필요한 곳에서만 사용
    • 함수가 자식 컴포넌트의 props로 전달되어 자식 컴포넌트의 불필요한 재렌더링을 방지할 때
    • 특정 함수가 자주 재생성될 때 성능 문제가 발생하는 경우
    • 의존성이 있는 함수이지만 불필요한 재생성을 막고자 할 때

상태값 캐싱 (useRef)

  • 상태 변경 시 렌더링 발생 X
  • React 렌더링 주기와 독립적인 참조를 유지하려할 때 적합
  • Vue의 shallowRef(반응성을 제공하되 내부 객체의 속성까지 깊은 반응성을 적용하지 않는 경우 사용)와 가장 비슷한 개념
  • 컴포넌트 내에서 상태를 유지하면서도 리렌더링에 영향을 주지 않으므로, 컴포넌트가 리렌더링될 때 값을 보존하는 데 활용
    • 이벤트 핸들러에게만 필요한 정보, 변경이 일어날 때 리렌더링이 필요하지 않음
    • current 라는 단일 객체 반환, ref의 current 값을 변조하면 다음과 같이 즉시 변경됨, state처럼 문자열, 객체, 심지어 함수 등 모든 것을 가리킬 수 있음
    • 컴포넌트가 일부 정보를 기억하고 싶지만, 해당 정보가 렌더링을 유발하지 않도록 하려면 ref 사용 (state는 값 변경에 따라 재 렌더링)
  const itemRef = useRef(초기값);
  itemRef.current = 변경값;
  • React가 관리하는 DOM 요소에 접근해야 할 때 ref 사용
  <script>
    // componentRef.current 로 DOM 접근
  </script>
  <template>
    <MyComponent ref={componentRef} />
  </template>

useState

  • 상태 변경 시 렌더링 발생
  • 객체의 얕은 반응성을 구현하려면 상태 업데이트 시 객체를 복사해야 함

TypeScript

리액트에서 제공하는 유틸리티 타입 중 '엘리먼트의 속성 타입'

  • @types/react 참고
  • ComponentPropsWithoutRef 사용, ref가 필요한 경우에만 forwardRef 사용해 컴포넌트 감싸주는 방식 추천
  • 기본 엘리먼트 속성
    • childern: ReactNode 타입
    • className: string 타입
    • style: CSSProperties 타입
    • id: string
  • 이벤트 핸들러
    • onClick: (event: React.MouseEvent<HTMLElement>) => void 타입
    • onChange: (event: React.ChangeEvent<HTMLInputElement>) => void 타입
    • onSubmit: (event: React.FormEvent<HTMLFormElement>) => void 타입
  • 특수 속성
    • ref: React.Ref<T> 타입
    • key: string | number 타입

컴포넌트 정의

  • HTML 기본 엘리먼트(div, button 등)를 래핑하는 컴포넌트를 만들 때,
    HTMLAttributes 또는 HTMLProps 대신 ComponentProps / ComponentPropsWithRef / ComponentPropsWithoutRef 사용하기
    참고
  • 리액트 컴포넌트를 구현하는 방식은 클래스 컴포넌트, 함수 컴포넌트로 크게 두 가지가 있음
  • 타입스크립트를 사용하여 함수 컴포넌트 방식으로 구현할 때 FC라는 제레릭 타입 존재, React.FCFC는 함수 컴포넌트 의미
  • React.FC
    • children prop 이 자동으로 추가 됨
    • defaultProps이 예상대로 작동하지 않음
    • Generic 타입을 컴포넌트에 적용하는 것이 더 복잡
    • 함수 오버로딩 지원 X
  • 컴포넌트 정의할 때 React.FC 대신 props 자체에 명시적인 함수 반환 타입을 사용하는 것을 선호

1. HTMLAttributes

  • key, ref 같은 props 타입이 정의되어있지 않기 때문에 사용하기에 적합하지 않음
  • 기본 속성(네이티브 HTML 속성, e.g. className, onClick, style)) 확장
  • 다이나믹 컴포넌트의 경우 React.HTMLAttributes<HTMLElement> 사용?
  • e.g.
    interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
      variant?: 'primary' | 'secondary'; // 추가적인 커스텀 속성
    }

2. [Element]HTMLAttributes<HTML[Element]>

  • 네이밍이 직관적이지 않음

3. ComponentPropsWithoutRef<T>

  • ComponentProps, ComponentPropsWithRef 보다 ComponentPropsWithoutRef 사용 권장
  • ComponentPropsWithRef 에서 ref 를 제거한 나머지 props
  • ref 를 받아야할 때는 forwardRef 로 컴포넌트 감싸서 사용
  • children을 명시적으로 받을 수 있도록 설계된 컴포넌트가 아니라면, ComponentPropsWithoutRef 타입으로 생성된 props 타입에 children이 포함되지 않음

4. ComponentPropsWithRef<T>

  • React에서 제공하는 유틸리티 타입으로, 특정 컴포넌트의 props와 ref(라이브러리 사용자가 컴포넌트에 직접적으로 접근할 수 있도록 허용)를 함께 정의 할 수 있음
  • 다음 속성을 포함
    • 기본 HTML 속성(style, className, id, title, lang, dir, tabIndex, onClick, onMouseEnter, onMouseLeave 등)
    • Accessibility 속성(aria-*)
    • Global 속성(dat-*)
    • Ref 속성(ref) 포함
  • e.g.
    interface SpanProps extends React.ComponentPropsWithRef<'span'> {
      variant?: 'primary' | 'secondary'; // 추가적인 커스텀 속성
    }

5. React.FunctionComponent (=React.FC)

  •   const ComponentName: React.FC<ComponentProps & React.HTMLAttributes<HTMLElement>> = () => {}
  • 단점: 자동으로 children props가 존재하게 됨, children Props의 타입 체킹이 명확히 되지 않음, 타입스크립트를 사용하는 의미가 있을까?

event

children

  • React.ReactNode
    • React의 모든 가능한 자식 요소를 포함하는 타입 (string, number, boolean, null, undefined, ReactElement, ReactFragment, ReactPortal 등 React에서 사용할 수 있는 모든 자식 요소 타입을 포괄)
    • 컴포넌트를 더 추상적으로 만들기 위해선 더 많은 타입을 수용할 수 있는 React.ReactNode 사용
    • React.ReactElement로 타입을 정의하면 string 을 받을 수 없고 오직 JSX 형식만 받을 수 있음
    • ReactNode는 ReactText를 포함하는 타입임
  • JSX.Element: JSX 표현식을 위한 타입
  • e.g.
    namespace JSX {
      interface Element extends React.ReactElement<any, any> {}
    }
  • ReactElement
    • 사실상 JSX.Element와 동일
    • 컴포넌트, createElement, cloneElement 등에 의해 렌더링 되어지는 값의 타입이지만 null을 포함하고 있지 않음
    • e.g.
    // ReactElement
    interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
      type: T;
      props: P;
      key: Key | null;
    }
  • JSX 내부에서 렌더링할 수 있는 모든 대상
    // ReactNode
    type ReactText = string | number;
    type ReactChild = ReactElement | ReactText;
    type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean |   null | undefined;
  • React.ReactDOM
    • React DOM 관련 API를 포함하는 객체
    • render, hydrate, createPortal 등의 React DOM 관련 기능을 제공하는 객체

style

  • React.CSSProperties는 React에서 인라인 스타일링을 할 때 타입 안정성과 개발 편의성 제공
  • CSS 속성과 값의 타입 검사를 지원하여 오류를 줄이고, 코드의 가독성과 유지 보수성을 높이는 데 유용
  • TypeScript를 사용한 React 프로젝트에서 style 속성을 사용할 때 적극 활용 권장

다형성 컴포넌트

  • 다형성 컴포넌트 구현 방법 중 1안 선택
    • 렌더링하고자 하는 태그 또는 컴포넌트명을 속성값으로 받아 컴포넌트 변수로 선언하여 JSX에서 사용
    • React.createElement 활용 (가이드상 지양)
  • NDK Nex Vue 에서 사용하던 tag속성 대신 as속성 활용
  • 컴포넌트 변수(ComponentTag) 선언하여 JSX에서 사용
const ComponentName = () => {
  const { as } = props;
  const Component = as; || 기본값;   
  return ( <Component>...</Component> )
}

패턴

render prop

다수의 자식 요소 렌더링

Named Children Pattern

<Card size="large">
{{
  title: "Hello World"
  subtitle: <>This is a basic <strong>example</strong></>
  body: "Here is where a lot more text would go."
}}
</Card>
function Card({ size = "medium", children }) {
  return (
    <div className={size}>
      <h2>{children.title}</h2>
      <h3>{children.subtitle}</h3>
      <p>{children.body</p>
    </div>
  )
}
  • React 생태계에서는 간단하고 직관적인 기본 props.children 권장이 일반적, 프로젝트의 복잡성이나 요구사항에 따라 적절히 사용
  • 장점
    • 읽기 쉽고 쉽게 변경할 수 있음
    • 자식 요소를 명확히 정의하는 데 도움
  • 단점
    • 자식 컴포넌트를 재구성할 때 메모이제이션 이슈 발생 가능
    • 부모 컴포넌트가 렌더링될 때마다 자식 요소를 포함하는 객체가 매번 새로 생성됨
    • children의 단순함과 JSX의 자연스러운 유연성을 잃을 수 있음

prop

  • React에서는 일반적으로 JSX를 프로퍼티로 전달하여 Vue의 slot과 동일한 패턴을 얻을 수 있음
  • 네이밍 규칙
    • 역할 기반 네이밍 (Role-based naming)
    • 위치 기반 네이밍 (Position-based naming)
    • 컨텍스트 기반 네이밍 (Context-based naming)
  • 일반 프로퍼티와의 구분을 위해
    • 접미사 활용 (e.g. headerContent, footerElement, customComponent.)
    • 타입 지정
  • https://sandroroth.com/blog/react-slots/
  • https://react.dev/reference/react-dom/components/common#common-props

    slot: A string. Specifies the slot name when using shadow DOM. In React, an equivalent pattern is typically achieved by passing JSX as props, for example <Layout left={} right={} />.

  • react JSX prop naming

Polymorphic Component (다형성 컴포넌트)

as

  • 다형성 컴포넌트 구현 시 as속성을 활용하는게 일반적

컴포넌트 변수 선언

  • e.g.
      const ComponentName = () => {
        const { as } = props;
        const Component = as; || "div";      
        return ( <Component>...</Component> )
      }

React.ElementType

  • 컴포넌트가 HTML 요소나 React 컴포넌트 중 어떤 것일지 미리 알 수 없을 때 사용
  • React에서 렌더링 가능한 컴포넌트나 HTML 태그의 타입을 정의하는 유틸리티 타입
  • string (e.g. 'div', 'span' 같은 HTML 태그), React 컴포넌트, 리액트 컴포넌트로 사용 가능한 모든 것을 포함
  • 렌더링할 태그나 컴포넌트를 동적으로 선택할 때 유용

React.ComponentType

  • 컴포넌트가 반드시 React 컴포넌트(함수형 또는 클래스형)임이 보장되어야 할 때 사용

React.createElement 활용

  • Legacy React APIs

컴포넌트 지연 로딩

  • React.lazySuspense 조합: React에서 컴포넌트의 지연 로딩을 구현할 때 사용
// LazyComponent.js
import React from 'react';

const LazyComponent = () => {
  return (
    <div>
      <h2>This is a lazy-loaded component!</h2>
    </div>
  );
};

export default LazyComponent;

import React, { Suspense, useState } from 'react';
// React.lazy를 사용해 LazyComponent를 동적으로 import
const LazyComponent = React.lazy(() => import('./LazyComponent'));

const App = () => {
  const [showLazyComponent, setShowLazyComponent] = useState(false);

  return (
    <div>
      <h1>React.lazy and Suspense Example</h1>
      
      <button onClick={() => setShowLazyComponent(!showLazyComponent)}>
        Toggle Lazy Component
      </button>

      {/* Suspense로 LazyComponent를 감싸서 로딩 상태를 보여줌 */}
      <Suspense fallback={<div>Loading...</div>}>
        {showLazyComponent && <LazyComponent />}
      </Suspense>
    </div>
  );
};

export default App;
  • asyncawait 조합
    • 비동기 함수의 결과가 필요할 때까지 기다리는 일반적인 JavaScript 방식
    • 비동기 작업: 서버에 데이터를 요청하거나 전송하는 API 호출, 파일 입출력(I/O), 타이머와 지연 함수 (setTimeout, setInterval 등), 데이터베이스 작업, 병렬 작업 관리 등

도구

SVGR

  • SVG를 React 컴포넌트로 변환해주는 툴
  • 변환 과정
    • SVGO를 사용하여 SVG 코드를 React 요소로 변환하기 전에 최적화
    • HTML 을 JSX 로 변환
    • JSX 를 React Component 로 랩핑
    • 바벨 AST를 코드로 변환
    • 프리터를 사용하여 코드 서식 지정
  • Node.js 에서의 사용
    • @svgr/plugin-jsx는 SVG를 React 컴포넌트로 변환할 때 핵심적인 역할을 하는 플러그인
  • options
  • 참고: SVG 변환 툴 (https://www.svgviewer.dev/)

성능


공식문서 - 학습하기

  • React 컴포넌트
    • 마크업을 반환하는 자바스크립트 함수
    • React 컴포넌트의 이름은 항상 대문자로 시작, HTML 태그는 소문자로 시작
    • 컴포넌트 정의 방법
        1. Component를 extending하는 클래스 컴포넌트
        1. JSX를 리턴하는 함수형 컴포넌트
  • React로 사고하기
    • state는 앱이 기억해야 하는, 변경할 수 있는 데이터의 최소 집합
    • state를 구조화하는 데 가장 중요한 원칙은 중복배제원칙(Don’t Repeat Yourself)
    • 애플리케이션이 필요로 하는 가장 최소한의 state를 파악하고 나머지 모든 것들은 필요에 따라 실시간으로 계산
      • 시간이 지나도 변하지 않으면 state X
      • 부모로부터 props를 통해 전달되면 state X
      • 컴포넌트 안의 다른 state나 props를 가지고 계산 가능하다면? state X
    • React는 항상 컴포넌트 계층구조를 따라 부모에서 자식으로 데이터를 전달하는 단방향 데이터 흐름
    • 반대 방향의 데이터 흐름을 만들기 위해서는 이벤트 핸들러 추가하여 set*함수 호출을 통해 부모 state를 변경
  • React의 모든 갱신은 두 단계로 나눌 수 있음
    • 렌더링 단계: React는 화면에 무엇을 그려야 하는지 알아내도록 컴포넌트를 호출합니다.
    • 커밋 단계: React는 변경사항을 DOM에 적용합니다.
  • 컴포넌트 내부의 2가지 로직 유형
    • 렌더링 코드를 주관하는 로직은 컴포넌트의 최상단에 위치, props와 state를 적절히 변형해 결과적으로 JSX를 반환, 렌더링 코드 로직은 순수해야 함, 수학 공식처럼 결과만 계산해야 하고 그 외에는 아무것도 하지 말아야 함
    • 이벤트 핸들러는 단순한 계산 용도가 아닌 무언가를 하는 컴포넌트 내부의 중첩 함수. 입력 필드를 업데이트하거나, 제품을 구입하기 위해 HTTP POST 요청을 보내거나, 사용자를 다른 화면으로 이동시킬 수 있음. 이벤트 핸들러에는 특정 사용자 작업(예: 버튼 클릭 또는 입력)으로 인해 발생하는 '부수 효과'(이러한 부수 효과가 프로그램 상태를 변경)를 포함 함

UI 표현하기

  • 컴포넌트에 props 전달하기
    • Props는 컴퓨터 과학에서 "변경할 수 없다"라는 의미의 불변성을 가지고 있습니다.
    • Props는 변경할 수 없습니다. 상호작용이 필요한 경우 state를 설정해야 합니다.

컴포넌트 순수하게 유지하기

  • 컴포넌트는 순수해야만 함
    • 렌더링전에 존재했던 객체나 변수를 변경하지 않음
    • 입력이 같을 경우, 컴포넌트는 항상 같은 JSX를 반환
  • React에는 렌더링하면서 읽을 수 있는 세 가지 종류의 입력 요소 "props, state, context"가 있으며, 이러한 입력 요소는 항상 읽기전용으로 취급해야 함
  • React에서 사이드 이펙트는 보통 이벤트 핸들러에 포함됨
  • 렌더링은 언제든지 발생할 수 있으므로 컴포넌트는 서로의 렌더링 순서에 의존하지 않아야 함

상호작용 더하기

  • 이벤트에 응답하기

    • 이벤트 핸들러로 전달한 함수들은 호출(onClick={handleClick()})이 아니라 아닌 전달(onClick={handleClick})되어야 함
    • 이벤트 핸들러에 적절한 HTML 태그를 사용하기
      • e.stopPropagation() 이벤트 핸들러가 상위 태그에서 실행되지 않도록 멈춤
        function Button({ onClick, children }) {
        return (
          <button onClick={e => {
            e.stopPropagation();
            onClick();
          }}>
            {children}
          </button>
        );
        }
    • e.preventDefault() 기본 브라우저 동작을 가진 일부 이벤트가 해당 기본 동작을 실행하지 않도록 방지
      export default function Signup() {
      return (
        <form onSubmit={e => {
          e.preventDefault();
          alert('Submitting!');
        }}>
          <input />
          <button>Send</button>
       </form>
      );
      }
  • State: 컴포넌트의 기억 저장소

    • React는 컴포넌트별 메모리를 state라고 부름
    • useState 훅
      • 렌더링 간에 데이터를 유지하기 위한 state 변수.
      • 변수를 업데이트하고 React가 컴포넌트를 다시 렌더링하도록 유발하는 state setter 함수
      • const [index, setIndex] = useState(0);
        // 이 경우 React가 index를 기억하기를 원합니다.
      • state는 선언한 컴포넌트에 완전히 비공개
      • 두 컴포넌트의 state가 항상 함께 변경되기를 원할 때는 각 컴포넌트에서 state를 제거하고 가장 가까운 공통 부모 컴포넌트로 옮긴 후 props로 전달하여 컴포넌트 간 State 공유하기
      • https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e
  • 렌더링 그리고 커밋

    • React 앱의 모든 화면 업데이트는 세 단계: 트리거, 렌더링, 커밋
    • 렌더링 결과가 이전과 같으면 React는 DOM을 건드리지 않음
  • 스냅샷으로서의 state

    • 인터페이스가 이벤트에 반응하려면 state를 업데이트해야 함
    • state는 함수가 반환된 후 사라지는 일반 변수와 다름
  • state 업데이트 큐

    • batching: React는 이벤트 핸들러가 실행을 마친 후 state 업데이트 처리
    • state setter 업데이트 함수 인수의 이름은 해당 state 변수의 첫 글자로 지정하는 것이 일반적
  • 객체 State 업데이트하기

    • 이미 state에 존재하는 객체를 변경할 때
    import { useState } from 'react';
    
    export default function MovingDot() {
      const [position, setPosition] = useState({
        x: 0,
        y: 0
      });
      return (
      // 아무 변화 X
      <div
        onPointerMove={e => {
          position.x = e.clientX;
          position.y = e.clientY;
        }}
      </div>
     // 리렌더링을 발생시키려면, 새 객체를 생성하여 state 설정 함수로 전달 해야 함
      <div
        onPointerMove={e => {
          setPosition({
            x: e.clientX,
            y: e.clientY
          });
        }}
      </div>
    )
    }
    • useImmer : state가 중첩되어 있고 객체를 복사하는 것이 중복되는 코드를 만들 때 유용
  • 배열 State 업데이트하기

    • React에서는, state의 객체나 배열을 변경하지 않는 게 좋기 때문에 slice 를 훨씬 더 자주 사용하게 될 것
      slice: 배열 또는 그 일부를 복사
      splice: 배열 변경 (항목을 추가하거나 제거)

State 관리하기

State를 사용해 input 다루기

  • React에서는 직접 UI를 조작할 필요가 없음
  • 상태를 업데이트할 때는 비동기적으로 처리되므로, 상태 값에 직접 접근하지 말고 항상 setState 함수로 변경해야 함
  • 상태 업데이트는 새로운 값으로 덮어쓰는 방식이므로, 이전 값을 기준으로 계산할 필요가 있을 때는 setState에 콜백 함수를 전달하는 것이 안전

State 구조 선택하기

  • 목표 "오류 없이 상태를 쉽게 업데이트하는 것"
  • 연관된 state 그룹화
    • 두 개 이상의 state 변수를 항상 동시에 업데이트한다면, 단일 state 변수로 병합하는 것을 고려.
  • 여러 state 가 서로 모순이 되고 불일치할 수 있는 방식으로 state 구성 피하기.
  • 불필요한 state 피하기
    • 렌더링 중에 컴포넌트의 props나 기존 state 변수에서 일부 정보를 계산할 수 있다면, 컴포넌트의 state에 해당 정보를 넣지 않아야 합니다.
    • Props를 state에 미러링 X
  • State의 중복 피하기. 여러 상태 변수 간 또는 중첩된 객체 내에서 동일한 데이터가 중복될 경우 동기화를 유지하기가 어려움.
  • 깊게 계층화된 state는 업데이트하기 쉽지 않습니다. 가능하면 state를 평탄한 방식으로 구성하여 깊게 중첩된 state 피하기.
    • 중첩된 state 업덷이트 하는 것은 변경된 부분부터 모든 객체의 복사본을 만드는 것을 의미
    • 자식의 배열을 가지는 트리 구조 대신, 각 노드가 자식 노드 ID의 배열을 가지는 테이블 구조 활용.
    • Immer 활용

컴포넌트 간 State 공유하기

  • 두 컴포넌트를 조정하고 싶을 때 state를 그들의 공통 부모(조정하려는 두 자식 컴포넌트의 가장 가까운 공통 부모 컴포넌트)로 이동
    -> 공통 부모로부터 props를 통해 정보를 전달
    -> 이벤트 핸들러를 전달해 자식에서 부모의 state를 변경할 수 있도록 함
  • e.g. Group Accordion

State를 보존하고 초기화하기

  • React 컴포넌트가 제거될 때, state도 함께 제거됨
  • 같은 함수에서 다른 컴포넌트를 렌더링할 때마다 React는 그 아래의 모든 state를 초기화합니다. 이런 문제를 피하려면 항상 컴포넌트를 중첩해서 정의하지 않고 최상위 범위에서 정의해야 합니다.
  • key
    • key 는 전역적으로 유일하지 않음, 오직 부모 안에서만 자리 명시
  • 제거된 컴포넌트의 state를 보존하기

state 로직을 reducer로 작성하기

  • Reducer는 현재 상태와 액션을 보고 새로운 상태를 결정하는 함수

  • useState -> useReducer

      1. state를 설정하여 React에게 '무엇을 할 지'를 지시하는 대신, 이벤트 핸들러에서 'action'을 전달하여 '사용자가 방금 한 일'을 지정
    // 이벤트 핸들러를 통해 'tasks를 설정'
    function handleAddTask(text) {
      setTasks([...tasks, {
        id: nextId++,
        text: text,
        done: false
      }]);
    }
    // useReducer: 이벤트 핸들러를 통해 'task를 추가/변경/삭제'하는 action을 전달
    function handleAddTask(text) {
      dispatch({
    		  // "action" 객체:
        // type: 컴포넌트마다 다른 값
        type: 'added', 
        // 다른 필드는 이곳에
        id: nextId++,
        text: text,
      });
    }
      1. reducer 함수 작성 (reducer 함수 안에서는 switch 문을 사용하는 게 규칙)
    function yourReducer(state, action) {
      // React가 설정하게될 다음 state 값을 반환합니다.
    }
      1. 컴포넌트에서 reducer 사용하기
    // useState
    const [tasks, setTasks] = useState(initialTasks);
    
    // useReducer
    const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
  • state를 업데이트하는 모든 로직을 reducer를 사용해 컴포넌트 외부의 단일 함수로 통합해 관리할 수 있음.

  • useStateuseReducer를 다음대로 섞고 매치해도 됨, 같은 컴포넌트 안에서도 섞어서 샤용 가능.

  • useImmerReducer

    • reducer는 순수해야 하기 때문에, 이 안에서는 state를 변경할 수 없으나, Immer에서 제공하는 특별한 draft 객체를 사용하면 안전하게 state를 변경할 수 있음
    • 내부적으로, Immer는 변경 사항이 반영된 draft로 state의 복사본을 생성

Context를 사용해 데이터를 깊게 전달하기

  • ing... ❗️

Reducer와 Context로 앱 확장하기

탈출구

  • React 컴포넌트에서 외부의 시스템을 제어하고 동기화해야하는 경우 처리 방법
    • 대부분의 애플리케이션 로직과 데이터 흐름은 이러한 기능에 의존해서는 안 됨
    • e.g. 브라우저 API를 사용해 input에 초점을 맞추거나, React 없이 구현된 비디오 플레이어를 재생 및 일시 정지하거나, 원격 서버에 연결해서 메시지를 수신해야 하거나 등

Ref로 값 참조하기

  • 컴포넌트가 일부 정보를 기억하고 싶지만, 해당 정보가 렌더링을 유발하지 않도록 하려면 useRef 를 활용해 ref 사용
    • e.g. 이벤트 핸들러에게만 필요한 정보이고 변경이 일어날 때 리렌더링이 필요하지 않다면, ref를 사용하는 것이 더 효율적일 수 있습니다.
  • ref.current
    • useRefcurrent 라는 단일 객체 반환
    • ref의 current 값을 변조하면 다음과 같이 즉시 변경됨
    • state처럼 문자열, 객체, 심지어 함수 등 모든 것을 가리킬 수 있음

Ref로 DOM 조작하기

  • React는 렌더링 결과물에 맞춰 DOM 변경을 자동으로 처리하기 때문에 컴포넌트에서 자주 DOM을 조작해야 할 필요는 없으나, 가끔 특정 노드에 포커스를 옮기거나, 스크롤 위치를 옮기거나, 위치와 크기를 측정하기 위해서 React가 관리하는 DOM 요소에 접근해야 할 때가 있음
  • 직접 다른 컴포넌트의 DOM 노드를 조작하는 것은 코드가 쉽게 깨지게 만들기 때문에, Ref 사용은 자제해야 함
  • 노출된 기능을 제한하고 싶을 때 useImperativeHandle 사용
    • useImperativeHandle 는 다음 3개의 인자를 받음
      • ref: 부모 컴포넌트에서 전달된 ref
      • 함수: 반환할 객체 정의. 이 객체는 부모 컴포넌트에서 ref.current를 통해 접근할 수 있는 값이나 메서드들
      • 의존성 배열 (옵션): 이 배열에 포함된 값이 변경될 때만 ref의 값이 업데이트됨
    • useImperativeHandleref의 사용을 더 유연하게 만들어 줌, React 함수형 컴포넌트에서 클래스형 컴포넌트처럼 특정 기능을 제공할 수 있게 함
    • 컴포넌트가 특정 메서드를 노출해야 할 때 (e.g. 부모 컴포넌트가 자식 컴포넌트의 내부 상태를 제어하거나 어떤 동작을 트리거해야 할 때) 유용
    • 직접적인 DOM 조작이 필요할 때 (DOM 요소를 조작해야 하는 경우에도 ref를 통해 DOM에 접근할 수 있도록 설정할 수 있음)
  • forwardRef를 통해 MyInput 컴포넌트 선언, <MyInput ref={inputRef} />으로 React가 대응되는 DOM 노드를 inputRef.current에 대입하도록 설정해야 ref 를 통한 DOM 접근 가능
    const MyInput = forwardRef((props, ref) => {
      return <input {...props} ref={ref} />;
    });
  • 임의의 값을 ref로 지정할 수 있습니다. 그러나 ref의 가장 일반적인 사용 사례는 DOM 엘리먼트에 액세스하는 것
  • Hook은 컴포넌트의 최상단에서만 호출되어야 하기 때문에 useRef를 반복문, 조건문 혹은 map() 안쪽에서 호출할 수 없음
    • sol) 부모 요소에서 단일 ref를 얻고, querySelectorAll과 같은 DOM 조작 메서드를 사용하여 그 안에서 개별 자식 노드 찾기, 하지만 이는 다루기가 힘들며 DOM 구조가 바뀌는 경우 작동하지 않을 수 있음
    • sol) ref 콜백: ref 어트리뷰트에 함수 전달, React는 ref를 설정할 때 DOM 노드와 함께 ref 콜백을 호출하며, ref를 지울 때에는 null을 전달
  • React에서 state 갱신은 큐에 쌓여 비동기적으로 처리됨, DOM 변경을 동기적으로 수행하고싶을 때 flushSync를 활용해 state 업데이트를 flushSync 호출로 감싸면 됨
  • 일반적으로 렌더링하는 중 ref에 접근하는 것을 원하지 않습니다. 첫 렌더링에서 DOM 노드는 아직 생성되지 않아서 ref.current는 null인 상태. 갱신에 의한 렌더링에서 DOM 노드는 아직 업데이트되지 않은 상태. React는 ref.current를 커밋 단계에서 설정. DOM을 변경하기 전에 React는 관련된 ref.current 값을 미리 null로 설정. 그리고 DOM을 변경한 후 React는 즉시 대응하는 DOM 노드로 다시 설정. 대부분 ref 접근은 이벤트 핸들러 안에서. ref를 사용하여 뭔가를 하고 싶지만, 그것을 시행할 특정 이벤트가 없을 때 Effect 가 필요할 수도 있습니다.
  • React가 관리하는 DOM 노드를 직접 바꾸려 하지 마세요. React가 관리하는 DOM 노드를 수정하려 한다면, React가 변경할 이유가 없는 부분만 수정하세요.

Effect (useEffect)

  • Effect커밋이 끝난 후에 화면 업데이트가 이루어지고 나서 실행됨, 이 시점이 React 컴포넌트를 외부 시스템(네트워크 또는 써드파티 라이브러리와 같은)과 동기화하기 좋은 타이밍.
  • 컴포넌트에 Effect를 무작정 추가하지 마세요. Effect는 주로 React 코드를 벗어난 특정 외부 시스템과 동기화하기 위해 사용됩니다. 이는 브라우저 API, 서드 파티 위젯, 네트워크 등을 포함합니다. 만약 당신의 Effect가 단순히 다른 상태에 기반하여 일부 상태를 조정하는 경우에는 Effect가 필요하지 않을 수 있습니다.
  • 기본적으로, Effect는 모든 렌더링 후에 실행됩니다. 이는 종종 원하는 동작이 아닐 수 있습니다:
  • Effect 가 필요 없는 경우
    • 외부 시스템이 관여하지 않는 경우 (e.g. 일부 props 또는 state가 변경될 때 컴포넌트의 state를 업데이트하려는 경우), Effect가 필요하지 않음.
    • 렌더링을 위해 데이터를 변환하는 데 Effect 필요 X
    • 사용자 이벤트를 처리하는 데 Effect 필요 X
    • state에 따라 일부 state를 조정하는 데는 Effect 필요 X

커스텀 Hook


공식문서 > 레퍼런스 / ING

공통 컴포넌트

Legacy React APIs


createContext, useContext

  • Vue 의 provide, inject 패턴을 React 에서는 createContext, useContext 훅을 활용해 구현
  • vue
// Parent.vue
<script setup>
import { provide } from 'vue';
provide('exampleKey', { value: 42 });
</script>

// Child.vue
<script setup>
import { inject } from 'vue';
const example = inject('exampleKey');
</script>
  • react
// ParentComponent.tsx
import React, { createContext, useContext } from 'react';

const ExampleContext = createContext({ value: 42 });

const ParentComponent: React.FC = ({ children }) => {
  return (
    <ExampleContext.Provider value={{ value: 42 }}>
      {children}
    </ExampleContext.Provider>
  );
};

// ChildComponent.tsx
import React, { useContext } from 'react';

const ChildComponent: React.FC = () => {
  const example = useContext(ExampleContext);
  return <div>{example.value}</div>;
};

React.createElement

  • React에서 JSX없이 컴포넌트 생성할 때 사용하는 메서드
  • 컴포넌트를 동적으로 생성해야 하거나, JSX를 사용할 수 없는 상황에서 유용
// ex. 동적 컴포넌트 생성
const type = isButton ? 'button' : 'a';
const element = React.createElement(
  type,
  { href: isButton ? null : 'https://example.com', className: 'link' },
  'Click me'
);

children

  • Children API 사용 지양, children prop 사용
  • children 값이 undefined일 때 React가 JSX.Element로 처리할 수 있도록 children = null 로 기본값 추가

Hooks

  • Vue 3에서 도입된 Composition API의 일부인 Vue Composables 과 유사하지만 다름.
    • 둘 다 재사용 가능한 로직을 함수로 캡슐화하고, 이를 통해 상태 관리와 로직 분리가 가능하다는 점에서는 비슷
    • Vue의 Composables는 더 유연하고 직관적인 라이프사이클 관리 및 반응형 데이터를 제공하는 반면, React Hooks는 함수형 컴포넌트 내에서만 작동하고 엄격한 규칙을 따름
  • Vue는 함수의 메모이제이션보다는 반응성을 관리하는데에 집중, VUe에서는 보통 함수 자체가 반응형이 아니기 때문에 메모이제이션이 필요하지 않는 경우가 많음. computed, ref, 일반적인 함수 패턴으로 React 에서와 같은 결과 얻을 수 있음
  • 'use'로 시작하는 함수들
  • 훅은 React가 오직 렌더링 중일 때만 사용할 수 있는 특별한 함수
  • 훅 은 컴포넌트의 최상위 수준 또는 커스텀 훅에서만 호출할 수 있습니다. 조건문, 반복문 또는 기타 중첩 함수 내부에서는 훅을 호출할 수 없습니다. 훅은 함수이지만 컴포넌트의 필요에 대한 무조건적인 선언으로 생각하면 도움이 됩니다. 파일 상단에서 모듈을 “import”하는 것과 유사하게 컴포넌트 상단에서 React 기능을 “사용”합니다.

useMemo (Performance Hooks)

  • https://ko.react.dev/reference/react/useMemo

  • React 에서 성능 최적화를 위해 제공되는 훅

  • 특정 값이 변경될 때까지 계산된 값을 메모이제이션하여, 불필요한 재계산 방지

  • 일반적으로 계산 비용이 큰 작업이나 재렌더링 시 매번 새로 생성되면 안 되는 값을 효율적으로 관리하기 위함

  • 첫 번째 인자: a, b가 변경되지 않으면 이전에 계산한 결과 그대로 반환, 두 번째 인자: '의존성 배열'로, 이 배열 안의 값이 변경될 때만 첫 번째 인자로 받은 함수 재호출

  • const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
  • 사용 e.g.

    import React, { useMemo } from 'react';
    
    function FilteredList({ items, filter }) {
      // 매번 새로운 배열 생성 방지
      const filteredItems = useMemo(() => {
        return items.filter((item) => item.includes(filter));
      }, [items, filter]);
    
      return (
        <ul>
          {filteredItems.map((item) => (
            <li key={item}>{item}</li>
          ))}
        </ul>
      );
    }
  • 주의

    • 메모이제이션 자체에도 비용이 있기 때문에 남용하면 오히려 복잡성을 증가시키고 성능에 악영향을 줄 수 있음
    • 순수 함수에만 사용, 사이드 이펙트를 일으키는 함수는 인자로 넣어서는 안됨

useCallback (Performance Hooks)

  • https://ko.react.dev/reference/react/useCallback

  • 특정 함수가 재생성되는 것을 방지하기 위해 사용

  • useMemo가 값을 메모이제이션하는 것과 비슷하게 useCallback은 함수를 메모이제이션 함

  • const memoizedCallback = useCallback(() => {
      doSomething(a, b);
    }, [a, b]);
  • 함수가 자식 컴포넌트의 props로 전달되거나, React 훅(ex. useEffect)의 의존성 배열에 포함될 때 유용.
    이때 함수가 매번 새로 생성되지 않도록 하여 불필요하면 자식 컴포넌트의 재렌더링 방지

  • 사용 e.g.

    import React, { useState, useCallback } from 'react';
    
    function ParentComponent() {
      const [count, setCount] = useState(0);
    
      // handleClick 함수는 useCallback을 사용하여 메모이제이션 됨, ParentComponent가 다시 렌더링되어도 매번 새로 생성되지 않기 때문에, ChildComponent를 불필요하게 다시 렌더링하지 않음
      const handleClick = useCallback(() => {
        console.log('Button clicked');
      }, []);
    
      return (
        <div>
          <button onClick={() => setCount(count + 1)}>Increase Count</button>
          <ChildComponent onClick={handleClick} />
        </div>
      );
    }
    
    function ChildComponent({ onClick }) {
      console.log('ChildComponent re-rendered');
      return <button onClick={onClick}>Click Me</button>;
    }
    import React, { useState, useEffect, useCallback } from 'react';
    
    function FetchDataComponent({ url }) {
      const [data, setData] = useState(null);
    
      const fetchData = useCallback(() => {
        fetch(url)
          .then((response) => response.json())
          .then((data) => setData(data));
      }, [url]);
    
      // 의존성 배열에 포함된 useEffect 함수
      useEffect(() => {
        fetchData();
      }, [fetchData]);
    
      return <div>{data ? JSON.stringify(data) : 'Loading...'}</div>;
    }
  • 주의

    • 경우에 따라 메모이제이션 오버헤드가 더 클 수가 있음, 자주 변경되지 않는 함수를 메모이제이션하면 오히려 성능에 좋지 않은 영향을 줄 수 있으므로, 필요한 경우에만 사용
    • 객체, 배열 등 참조형 데이터가 의존성에 포함되면 매번 새로 생성되는 참조로 인해 메모이제이션이 제대로 되지 않을 수 있음, 이럴 경우 useMemo로 참조형 데이터를 관리하는 것이 좋음

useState

useEffect


https://react-typescript-cheatsheet.netlify.app/

기본 숙지 필요
: https://react.dev/
: https://www.typescriptlang.org/docs/handbook/2/basic-types.html
: https://www.typescriptlang.org/docs/handbook/2/everyday-types.html

Function Components
https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components/
이러한 함수는 props 인수를 사용하고 JSX 요소를 반환하는 일반 함수로 작성될 수 있습니다.

const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
  <div>{message}</div>
);

React.FC 는 기본 function과 다르게 return type을 명시한다.
displayName, propTypes, defaultProps와 같은 정적 property의 타입체크와 자동완성을 제공한다.
React.FC에서 defaultPropes를 사용할때 알려진 몇가지 문제가 있다.
React 18이 업데이트 되기 전, React.FC는 children의 암묵적인 정의를 제공했다.
이것은 논쟁중인 이유이며 CreateReact App TypeScript 템플릿에서 FC가 제거된 이유이다.
React.FC 대신 보통 함수를 사용해라.

type Props = { foo: string };

// OK now, in future, error
const FunctionComponent: React.FunctionComponent<Props> = ({
  foo,
  children,
}: Props) => {
  return (
    <div>
      {foo} {children}
    </div>
  ); // OK
};

// Error now, in future, deprecated
const VoidFunctionComponent: React.VoidFunctionComponent<Props> = ({
  foo,
  children,
}) => {
  return (
    <div>
      {foo}
      {children}
    </div>
  );
};

Class Components
React.Component는 일반 유형으로, 다음과 prop과 state를 매개변수로 제공합니다.

type MyProps = {
  // using `interface` is also ok
  message: string;
};
type MyState = {
  count: number; // like this
};
class App extends React.Component<MyProps, MyState> {
  state: MyState = {
    // optional second annotation for better type inference
    count: 0,
  };
  render() {
    return (
      <div>
        {this.props.message} {this.state.count}
      </div>
    );
  }
}

React component guide : Class vs Functional
https://www.educative.io/blog/react-component-class-vs-functional
React 버전 16.8에서는 클래스 구성 요소가 기능 구성 요소로 대체되었습니다.

참고 프로젝트
https://react.fluentui.dev/?path=/docs/concepts-introduction--page

React.js란?

React.js는 애플리케이션에서 UI를 구현하는데 사용되는 JS 프레임워크

특징

  • 컴포넌트 기반
  • Virtual DOM 사용
  • 단방향 데이터 바인딩 사용
  • build가 쉬운 SPA 프레임워크
  • 심플하지만 강력
  • 선언적
  • Server-side 지원

참고

  • react 자체는 라이브러리이지만 react 생태계에서는 프레임워크라고 볼 수 있음
  • react(페이스북에서 만듬), vue, angular, svelte 등 모두 SingPageApplication을 만들기 위한 라이브러리/프레임워크
  • 실무에서는 webpack, vite, babel 등으로 빌드 / 최종 결과물은 결국 html, css, js
    (babel이 JSX 코드 -> React.createElement 로 바꿔줌)
  • react도 결국 js다.
  • react는 데이터 중심으로 움직인다.

장점

  • 데이터와 화면 일치 문제해결(데이터를 바꾸면 화면이 바뀌어야 함)
  • 화면의 깜박임 해결

단점

  • 검색엔진 노출의 어려움

v17 vs v18


React 입문 강의

https://www.inflearn.com/course/web-game-react
(2022년 react 18 버전 기준 / but 아직 실무에서는 17이 더 흔히 보일 것)

  • create react app : 실무에서도 사용하지만 강의에서는 원시적인 형태부터 진행 할 예정
  • 리액트 감 잡기 : 아무 페이지에서 state 찾기 (바뀌는 것)
  • react가 화면을 만들어주고 우리는 데이터에만 신경 쓰면 됨, document.querySelector 이런건 사용 X (돔에 직접 접근하고싶을때는 ref 사용)
  • setState할때는 render 함수 실행 됨
  • render에 함수 적어주면 새로 계속 함수가 생성해줌
  • react는 데이터가 바뀌면 화면이 바뀜, 화면에 바뀔 부분들을 state로 만들어 둠
  • 객체를 함부로 바꾸지 마라(불변성), 객체를 복사해서 데이터 바꿔 사용 / pop, push 등 사용 X concat 등 사용 (함수, 배열 등 도 객체)
  • 컴포넌트가 리렌더링되는 경우
    • state 바뀔 때
    • props 바뀔 때
    • 부모컴포넌트가 리렌더링되면 자식컴포넌트도 리렌더링됨
    • but 값이 안바껴도 useState 함수 호출만하면 렌더링이 일어남

컴포넌트 제작

ReactDOM.render(<LikeButton/>, document.querySelector('#root')); // react 17
ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>); // react 18
  • 컴포넌트란 데이터와 화면을 하나로 묶으둔 것
  • 컴포넌트 제작 방식 : class 방식(요즘 안씀 ErrorBoundary 에서 1% 정도 사용) vs 함수 방식

JSX

  • html 보다 더 엄격
  • js코드는 {}로 감싸줘야함
  • 닫는태그 꼭 필요 (ex. <input/>)
  • for, if 문 사용 불가로,대신 삼항 연산자, 배열의 map 많이 씀
  • return 에서는 상위 태그가 하나여야 함 (fraament <></> 사용 가능)
  • class 대신 className 사용, for대신 htmlFor 사용
  • 주석 : {/**/}
  • 가독성있게 코딩하는게 약간 어려움
  • JSX에서 null은 태그가 아예없음 의미

컴포넌트 제작 방식 - class

  • 구 state로 신 state 만들때는 함수형 setState 사용해야 문제가 안생김
  • React.createRef() 사용하면 리액트훅의 ref와 유사하게 사용 가능 (.current. 로 접근 가능)

컴포넌트 제작 방식 - React Hooks

  • 함수 컴포넌트(함수형컴포넌트 아님, this 쓸 일이 없음)에 state, ref 기능을 추가한 것
  • class 컴포넌트 대신으로 사용, React는 Hooks 권장
  • setState 대신 useState 사용
  • 함수컴포넌트 특성상 코드 전체가 렌더링될 때 마다 계속 실행됨 (useState에 함수 넣을 때 주의 : lazy init)
  • state를 바꾸면 함수 전체가 다시 실행하기 때문에 조금 더 느릴 수 있음
  • memo() 는 부모 컴포넌트가 리렌더링될 때 자식 컴포넌트가 리렌더링되는 것 막아줌
  • ref 사용법이 클래스 컴포넌트와 다름
function LikeButton() {
  const [liked, setLiked] = React.useState(false); // setLiked는 liked 전용 setState
  if(liked) {
    // 뭔짓을 하든 결국 return 하는게 화면
    return 'You liked this.';
  }
  return (
    <button onClick={()=>{setLiked(true);}}>Like</button>
  )
}

state

  • render 안에서 state바꿔주면 무한 루프

컴포넌트분리 & props

  • 반복문 단위로 분리하는 경우가 많음
  • 부모컴포넌트 -> 자식컴포넌트 (this.props.*) props로 데이터 전달
  • 컴포넌트 props가 있다면 '부모가 있겠구나' 생각하기
  • 리액트의 대부분 문제는 prop에서 생 / 고조할아버지->나, 나->증조할아버지 막 이런식으로 props 관계가 복잡해서 이를 관리하기위해서 생긴게 redux, context, mbox 등
  • props는 부모가 바꿔야 함 자식이 바꾸는건 금지
    • props를 자식에서 바꿔야하는 경우 props를 자식컴포넌트의 state에 넣어주고 state를 바꿔줌 / 그래야 부모한테 영향이 안감
  • contextAPI
    • A 컴포넌트 > B 컴포넌트 > C 컴포넌트 > ... > G 컴포넌트
      A -> G로 바로 props를 넘기고 싶으면? context 사용 (context를 좀 더 응용한건 redux)
    • props를 받는다는건 렌더링이 될 수도 있다는 것임

ref

  • 돔에 직접 접근할 때 사용
  • hook에서는 this의 값을 접근할 때도 사용
  • setState하면 return 재실행 (리렌더링) / useRef() 값을 바꾸면 return 재실행 X (리렌더링 X) 값은 바뀌지만 화면에는 영향을 미치지 않을 때 timeout, interval 은 보통 useRef로 쓰임(ref면 .current로 접근 해야 함)

... 5-1. 리액트 라이프사이클 소개 ING ...

React Devtools & 성능

  • 바뀌지않는데 렌더링되는 부분이 있다면 성능 이슈 야기
  • shouldComponentUpdate 사용해서 언제 렌더링할지 추가 처리
    • PureComponent(클래스 컴포넌트에서만 사용)는 shouldComponentUpdate를 구현해둔 컴포넌트(state 바뀜 유/무 확인, 배열이나 객체와 같은 참조관계가 있는 경우 주의(참조관계가 바껴야 인지), state에서는 객체안쓰는게 좋음) 부모컴포넌트가 리렌더링되면 자식컴포넌트도 리렌더링됨 / 컴포넌트가 복잡해지면 PureComponent 못 쓰는 경우도 있음
    • 리액트 훅에서는 memo() 사용하면 부모 컴포넌트가 리렌더링될때 자식 컴포넌트가 리렌더링되는 문제 막아줌 / displayName 으로 컴포넌트 이름 다시 바꿔줘야 함

기타

  • 컨트롤드 인풋 vs 언컨트롤드 인풋 (컨트롤드 인풋 권장, 언컨트롤드 인풋 사용하는 케이스 정의되어 있음)
// 컨트롤드 인풋
<input
  ref={inputEl}
  value={value}
  onChange={{(e)=> setValue(e.currentTarget.value)}}
/>
// 언컨트롤드 인풋 (value를 onSubmit 안에서만 사용하는 케이스, value X)
<input defaultValue="value를 넣으면 컨트롤드 인풋으로 간주" ref={inputEl}/>
  • require: 노드의 모듈 시스템 CommonJS, node에서는 require 만 지원
    import: ES15 모듈 시스템, react 에서는 import 사용
  • 리액트 렌더링 기준은 구 state, 현 state 가 다를 때 (참조가 달라야 함)
arr1.push(1) // 리액트가 감지 X
const arr2 = [...arr1, 1] // 리액트가 감지 O
  • render 의 return 안(JSX)에서는 for, if를 못씀 / 대신 삼항연산자, && 연산자 등 사용

React 강의

https://www.inflearn.com/course/react-typescript-webgame

읽을거리

Top 10 React Component Libraries/Frameworks for 2022
https://velog.io/@warmwhiten/Top-10-React-Component-LibrariesFrameworks-for-2022-%EB%B2%88%EC%97%AD

profile
하루 모아 평생 🧚🏻

0개의 댓글