이벤트 위임 : 이러한 이벤트 단계의 원리를 이용해 이벤트를 상위 컴포넌트에만 붙이는 것
import React from 'react'
가 필요했다.// 구 버전
const Component = (
<div>
<span>hello world</span>
</div>
);
var Component = React.createElement(
// React를 import해야 한다.
"div",
null,
React.createElement("span", null, "hello world")
);
// 17 버전
("use strict");
var _jsxRuntime = require("react/jsx-runtime"); // React를 import하지 않아도 된다.
var Component = (0, _jsxRuntime.jsx)("div", {
children: (0, _jsxRuntime.jsx)("span", {
children: "hello world",
}),
});
이벤트 풀링 제거
e.persist()
가 필요했음export default function App() {
const [value, setValue] = useState("");
function handleChange(e: ChangeEvent<HTMLInputElement>) {
e.persist(); // e에 접근하기 위해 필요
setValue(() => {
return e.target.value;
});
}
return <input onChange={handleChange} value={value} />;
}
import {useId} from 'react'
function Child(){
const id = useId()
return <div>child: {id}</div>
}
function SubChild(){
const id = useId()
return (
<div>
SubChild : {id}
<Child/>
</div>
)
}
// 서버사이드렌더링 HTML
<div>
<div>
SubChild :Ram:
<div/>
<div>
child :R7am:
</div>
</div>
const [isPending, startTransition] = useTransition();
export default function Input() {
const [text, setText] = useState("");
const deferredText = useDeferredValue(text);
const list = useMemo(() => {
const arr = Array.from({ length: deferredText.length }).map(
(_) => deferredText
);
return (
<ul>
{arr.map((str, index) => (
<li key={index}>{str}</li>
))}
</ul>
);
}, [deferredText]);
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setText(e.target.value);
}
return (
<>
<input value={text} onChange={handleChange} />
{list}
</>
);
}
useSyncExternalStore(
subscribe: (callback) => Unsubscribe,
getSnapshot: () => State
) => State
import { useSyncExternalStore } from "react";
function subscribe(callback: (this: Window, ev: UIEvent) => void) {
window.addEventListener("resize", callback);
return () => {
window.removeEventListener("resize", callback);
};
}
export default function App() {
const windowSize = useSyncExternalStore(
subscribe,
() => window.innerWidth,
() => 0 // 서버 사이드 렌더링 시 제공되는 기본값. 서버 사이드에서는 해당 값을 추적할 수 없다.
);
return <>{windowSize}</>;
}
리액트 18 버전에서는 리액트 트리를 만들 때 사용되는 API 가 변경되었다.
// before
import ReactDOM from "react-dom";
import App from "App";
const container = document.getElementById("root");
ReactDOM.render(<App />, container);
// after
import ReactDOM from "react-dom";
import App from "App";
const container = document.getElementById("root");
const root = ReactDOM.createRoot(container);
root.render(<App />);
SSR 애플리케이션에서 하이드레이션을 하기 위한 새로운 메서드
// before
import ReactDOM from "react-dom";
import App from "App";
const container = document.getElementById("root");
ReactDOM.hydrate(<App />, container);
// after
import ReactDOM from "react-dom";
import App from "App";
const container = document.getElementById("root");
const root = ReactDOM.hydrateRoot(container, <App />);
import * as React from 'react'
function render(url, res){
let didError = false
const data = createServerData() // 서버에서 필요한 데이터를 불러온다.(오랜시간이 걸린다고 가정)
const stream = renderToPipeableStream(
// 데이터를 contextAPI로 넘긴다.
<DataProvider data={data}>
<App assets={asstes}>
</DataProvider>,
{
// 렌더링 시에 포함시켜야 할 JS 번들
bootstrapScripts: [asstes['main.js']],
onShellReady(){
// 에러 발생 시 처리 추가
res.statusCode = didError ? 500 : 200
res.setHeader('Content-type', 'text/html')
stream.pipe(res)
},
onError(x){
didError = true
console.error(x)
},
},
)
// 렌더링 시작 이후 일정 시간이 흐르면 렌더링에 실패한 것으로 간주하고 취소한다.
setTimeout(() => stream.abort(), ABORT_DELAY)
}
export default function App ({assets}){
return (
<Html assets={assets} title="Hello">
<Suspense fallback={<Spinner/>}>
<ErrorBoundary FallbackComponent={Error}> // 아직 데이터를 불러오는 중이라면 fallback으로 받음
<Content/>
</ErrorBoundary>
</Suspense>
</Html>
)
}
리액트가 여러 상태 업데이트를 하나의 리렌더링으로 묶어서 성능을 향상시키는 방법
import { flushSync } from "react-dom";
function handleClick() {
flushSync(() => {
setCounter((c) => c + 1);
});
flushSync(() => {
setFlag((f) => !f);
});
}
더 이상 안전하지 않은 특정 생명주기를 사용하는 컴포넌트에 대한 경고
문자열 ref 사용 금지
createRef가 없어도 컴포넌트 내부에서 문자열로 ref 생성 -> DOM 노드를 참조하는 것
class UnsafeClassComponent extends Component {
componentDidMount() {
console.log(this.refs.myInput);
}
render() {
return (
<div>
<input type="text" ref="myInput" />
</div>
);
}
}
문제점
findDOMNode 경고 출력
findDOMNode : 클래스 컴포넌트 인스턴스에서 실제 DOM 요소에 대한 참조를 가져올 수 있는 메서드
class UnsafeClassComponent extends Component {
componentDidMount(){
const node = ReactDOM.findDOMNode(this)
if(node){
;(node as HTMLDivElement).style.color = 'red'
}
}
render(){
return <div>UnsafeClassComponent</div>
}
}
문제점
구 ContextAPI 사용 시 발생하는 경고
예상치 못한 부작용(side-effects) 검사
컴포넌트를 동적으로 가져올 수 있게 도와주는 기능
export default function SampleComponent(){
return <>동적으로 가져오는 컴포넌트</>
}
// app.tsx
import {Suspense, lazy} from 'react'
const DynamicSampleComponent = lazy(() => import ('./SampleComponent')) // 컴포넌트를 최초 렌더링 이후 지연시켜 불러오는 역할. lazy와 Suspense는 한 쌍으로 사용되었다.
export default function App(){
return (
<Suspense fallback={<>로딩중</>}>
<DynamicSampleComponet/>
</Suspense>
)
}