name
속성 필수change
: 값이 변경될 때 이벤트 발생 텍스트 입력의 경우 포커스를 잃을 때 실행input
: 텍스트가 입력될 때마다 이벤트 발생 change와 달리 즉시 실행value
, onChange
, defaultValue
모두 없으면 비제어 상태의 HTML 표준 동작으로 처리value
(또는 체크박스와 라디오에는 checked
) prop 을 전달null
이나 undefined
가 아닌 문자열 value를 받아야 함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} />
);
};
defaultChecked
: 불리언 타입, type="checkbox"
와 type="radio"
input
에 대한 초기값 지정defaultValue
: 문자열 타입, 텍스트 input에 대한 초기값 을 지정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>
</>
);
};
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;
// 부모 컴포넌트
<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> ));
npm i react-router
Link
는 기본 라우팅 동작만을 제공, 더 가볍고 간단함NavLink
는 활성화 상태를 기반으로 동적 스타일과 클래스를 제공하는 추가 기능이 있음Route
객체의 element
속성으로 렌더링될 컴포넌트 지정npm i next
Link
객체 활용next/router
모듈에서 useRouter
훅을 불러와 사용React 컴포넌트는 마크업으로 뿌릴 수 있는 JavaScript 함수
React 컴포넌트의 이름은 항상 대문자로 시작, HTML 태그는 소문자로 시작
컴포넌트 정의 방법 중 2안 선택
컴포넌트 구현
const ComponentName = (props: ComponentProps) => {
const {
속성명 = 기본값,
className = '',
children,
...reset
} = props;
...
return ( <ComponentName {...reset}> ... </ComponentName> )
}
export default ComponentName;
const ComponentName = forwardRef((props: ComponentProps, ref) => {
const {
속성명 = 기본값,
...reset
} = props;
...
return ( <ComponentName ref={ref} {...reset}> ... </ComponentName> )
})
export default ComponentName;
useTransition
useDeferredValue
useDefferedValue
사용.useId
)useCallback
, useMemo
로 인한 성능 개선은 미미하거나 오히려 성능 저하를 유발할 수 있음useMemo
)computed
-> React의 useMemo
훅useMemo
는 첫 번째 렌더링에서의 속도 차이가 없음, 업데이트 시 불필요한 작업을 건너뛰는 데만 도움이 됨console.time('test');... console.timeEnd('test');
활용해 코드 소요시간 측정, 전체 기록 시간이 1ms이상이라면 해당 계산 메모이제이션하길 권장useMemo
를 사용하지 않으면 매번 새로 계산하지만 useMemo
를 사용하는 것도 성능에 영향을 주기 때문에 복잡한 연산을 캐싱할 때만 사용useCallback
)useRef
)ref
사용 (state
는 값 변경에 따라 재 렌더링) const itemRef = useRef(초기값);
itemRef.current = 변경값;
ref
사용 <script>
// componentRef.current 로 DOM 접근
</script>
<template>
<MyComponent ref={componentRef} />
</template>
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
타입FC
라는 제레릭 타입 존재, React.FC
의 FC
는 함수 컴포넌트 의미React.FC
children
prop 이 자동으로 추가 됨defaultProps
이 예상대로 작동하지 않음React.FC
대신 props
자체에 명시적인 함수 반환 타입을 사용하는 것을 선호HTMLAttributes
React.HTMLAttributes<HTMLElement>
사용?interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary'; // 추가적인 커스텀 속성
}
[Element]HTMLAttributes<HTML[Element]>
ComponentPropsWithoutRef<T>
ComponentProps
, ComponentPropsWithRef
보다 ComponentPropsWithoutRef
사용 권장ComponentPropsWithRef
에서 ref
를 제거한 나머지 propsref
를 받아야할 때는 forwardRef
로 컴포넌트 감싸서 사용children
을 명시적으로 받을 수 있도록 설계된 컴포넌트가 아니라면, ComponentPropsWithoutRef
타입으로 생성된 props 타입에 children
이 포함되지 않음ComponentPropsWithRef<T>
ref
(라이브러리 사용자가 컴포넌트에 직접적으로 접근할 수 있도록 허용)를 함께 정의 할 수 있음interface SpanProps extends React.ComponentPropsWithRef<'span'> {
variant?: 'primary' | 'secondary'; // 추가적인 커스텀 속성
}
const ComponentName: React.FC<ComponentProps & React.HTMLAttributes<HTMLElement>> = () => { … }
React.SyntheticEvent
https://ko.react.dev/learn/typescript#typing-dom-eventsReact.ReactNode
React.ReactNode
사용React.ReactElement
로 타입을 정의하면 string 을 받을 수 없고 오직 JSX 형식만 받을 수 있음JSX.Element
: JSX 표현식을 위한 타입namespace JSX {
interface Element extends React.ReactElement<any, any> {}
}
ReactElement
// ReactElement
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
// ReactNode
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
React.ReactDOM
React.CSSProperties
는 React에서 인라인 스타일링을 할 때 타입 안정성과 개발 편의성 제공다형성 컴포넌트
React.createElement
활용 (가이드상 지양) tag
속성 대신 as
속성 활용ComponentTag
) 선언하여 JSX에서 사용const ComponentName = () => {
const { as } = props;
const Component = as; || 기본값;
return ( <Component>...</Component> )
}
<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>
)
}
props.children
권장이 일반적, 프로젝트의 복잡성이나 요구사항에 따라 적절히 사용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={} />.
as
as
속성을 활용하는게 일반적 const ComponentName = () => {
const { as } = props;
const Component = as; || "div";
return ( <Component>...</Component> )
}
React.createElement
활용React.lazy
와 Suspense
조합: 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;
async
와 await
조합@svgr/plugin-jsx
는 SVG를 React 컴포넌트로 변환할 때 핵심적인 역할을 하는 플러그인Component
를 extending하는 클래스 컴포넌트 JSX
를 리턴하는 함수형 컴포넌트set*
함수 호출을 통해 부모 state를 변경onClick={handleClick()}
)이 아니라 아닌 전달(onClick={handleClick}
)되어야 함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
변수.state setter
함수const [index, setIndex] = useState(0);
// 이 경우 React가 index를 기억하기를 원합니다.
state setter
업데이트 함수 인수의 이름은 해당 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가 중첩되어 있고 객체를 복사하는 것이 중복되는 코드를 만들 때 유용slice
를 훨씬 더 자주 사용하게 될 것slice
: 배열 또는 그 일부를 복사splice
: 배열 변경 (항목을 추가하거나 제거)key
Reducer는 현재 상태와 액션을 보고 새로운 상태를 결정하는 함수
useState
-> useReducer
// 이벤트 핸들러를 통해 '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,
});
}
function yourReducer(state, action) {
// React가 설정하게될 다음 state 값을 반환합니다.
}
// useState
const [tasks, setTasks] = useState(initialTasks);
// useReducer
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
state를 업데이트하는 모든 로직을 reducer를 사용해 컴포넌트 외부의 단일 함수로 통합해 관리할 수 있음.
useState
와 useReducer
를 다음대로 섞고 매치해도 됨, 같은 컴포넌트 안에서도 섞어서 샤용 가능.
useRef
를 활용해 ref
사용 ref.current
useRef
는 current
라는 단일 객체 반환Ref
사용은 자제해야 함useImperativeHandle
사용useImperativeHandle
는 다음 3개의 인자를 받음ref
: 부모 컴포넌트에서 전달된 ref함수
: 반환할 객체 정의. 이 객체는 부모 컴포넌트에서 ref.current
를 통해 접근할 수 있는 값이나 메서드들useImperativeHandle
은 ref
의 사용을 더 유연하게 만들어 줌, React 함수형 컴포넌트에서 클래스형 컴포넌트처럼 특정 기능을 제공할 수 있게 함forwardRef
를 통해 MyInput
컴포넌트 선언, <MyInput ref={inputRef} />
으로 React가 대응되는 DOM 노드를 inputRef.current
에 대입하도록 설정해야 ref 를 통한 DOM 접근 가능const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
useRef
를 반복문, 조건문 혹은 map()
안쪽에서 호출할 수 없음querySelectorAll
과 같은 DOM 조작 메서드를 사용하여 그 안에서 개별 자식 노드 찾기, 하지만 이는 다루기가 힘들며 DOM 구조가 바뀌는 경우 작동하지 않을 수 있음ref
어트리뷰트에 함수 전달, React는 ref를 설정할 때 DOM 노드와 함께 ref 콜백을 호출하며, ref를 지울 때에는 null을 전달flushSync
를 활용해 state 업데이트를 flushSync 호출로 감싸면 됨useEffect
)Effect
는 커밋이 끝난 후에 화면 업데이트가 이루어지고 나서 실행됨, 이 시점이 React 컴포넌트를 외부 시스템(네트워크 또는 써드파티 라이브러리와 같은)과 동기화하기 좋은 타이밍.dangerouslySetInnerHTML
provide
, inject
패턴을 React 에서는 createContext
, useContext
훅을 활용해 구현// 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>
// 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>;
};
// ex. 동적 컴포넌트 생성
const type = isButton ? 'button' : 'a';
const element = React.createElement(
type,
{ href: isButton ? null : 'https://example.com', className: 'link' },
'Click me'
);
children
값이 undefined
일 때 React가 JSX.Element로 처리할 수 있도록 children = null
로 기본값 추가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>
);
}
주의
특정 함수가 재생성되는 것을 방지하기 위해 사용
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
로 참조형 데이터를 관리하는 것이 좋음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는 애플리케이션에서 UI를 구현하는데 사용되는 JS 프레임워크
특징
참고
장점
단점
v17 vs v18
https://www.inflearn.com/course/web-game-react
(2022년 react 18 버전 기준 / but 아직 실무에서는 17이 더 흔히 보일 것)
ReactDOM.render(<LikeButton/>, document.querySelector('#root')); // react 17
ReactDOM.createRoot(document.querySelector('#root')).render(<LikeButton/>); // react 18
JSX
<input/>
)<></>
사용 가능){/**/}
컴포넌트 제작 방식 - class
컴포넌트 제작 방식 - React Hooks
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
컴포넌트분리 & props
ref
... 5-1. 리액트 라이프사이클 소개 ING ...
// 컨트롤드 인풋
<input
ref={inputEl}
value={value}
onChange={{(e)=> setValue(e.currentTarget.value)}}
/>
// 언컨트롤드 인풋 (value를 onSubmit 안에서만 사용하는 케이스, value X)
<input defaultValue="value를 넣으면 컨트롤드 인풋으로 간주" ref={inputEl}/>
arr1.push(1) // 리액트가 감지 X
const arr2 = [...arr1, 1] // 리액트가 감지 O
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