반응형
개발하는데 개인적으로 필요한 내용하고 제가 이해한 방식만 적었기 때문에 필요 사항은 공식 문서 참조
React Blog – React
The library for web and native user interfaces
react.dev
📝컴파일러 개선
useMemo, useCallaback를 사용한 최적화 작업을 안 할 수 있도록 컴파일러가 자동 처리하게끔 설정하지만 정말 필요한 상황에선 써야하기 때문에 useMemo와 useCallback은 19버전 메뉴얼에도 제공함
📝useTransition
기존 방식
// Before Actions
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, setIsPending] = useState(false);
const handleSubmit = async () => {
setIsPending(true);
const error = await updateName(name);
setIsPending(false);
if (error) {
setError(error);
return;
}
redirect("/path");
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
}
useTransition 사용
// Using pending state from Actions
function UpdateName({}) {
const [name, setName] = useState("");
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();
const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
})
};
return (
<div>
<input value={name} onChange={(event) => setName(event.target.value)} />
<button onClick={handleSubmit} disabled={isPending}>
Update
</button>
{error && <p>{error}</p>}
</div>
);
함수내에서 isPending으로 인한 Pending 작업 축소화
📝form 개선
// useFormStatus (form 정보 가져와서 읽기)
import { useFormStatus } from "react-dom";
import action from './actions';
function Submit() {
const status = useFormStatus();
return <button disabled={status.pending}>Submit</button>
}
export default function App() {
return (
<form action={action}>
<Submit />
</form>
);
}
form을 잘 활용할 수 있게 다양한 편의성 업데이트가 있지만 개인적인 의견으로 복잡한 상황에는 useForm을 쓰는 게 더 나아보인다.
📝use을 이용해 fetch 보일러 플레이트 개선
// 서버 컴포넌트 (page.tsx)
import { getPosts } from './lib/api';
export default async function Page() {
const posts = await getPosts();
return <PostList posts={posts} />;
}
// 클라이언트 컴포넌트 (useState + useEffect 필요)
"use client";
function PostList({ posts: initial }) {
const [posts, setPosts] = useState(initial);
useEffect(() => {
async function fetchData() {
const data = await fetch('/api/posts').then(res => res.json());
setPosts(data);
}
fetchData();
}, []);
return (
<ul>
{posts.map(p => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
// 서버 컴포넌트 (page.tsx)
import { use } from 'react';
function fetchPosts() {
return fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json());
}
export default function Page() {
const posts = use(fetchPosts()); // ✅ 한 줄로 await처리
return (
<ul>
{posts.map((p: any) => (
<li key={p.id}>{p.title}</li>
))}
</ul>
);
}
상위에 선언한 Context를 가져오거나 Fetch Promise를 넣어서 코드를 간결하게 작성 가능
📝React 정적 랜더링
import { prerender } from 'react-dom/static';
async function handler(request) {
const {prelude} = await prerender(<App />, {
bootstrapScripts: ['/main.js']
});
return new Response(prelude, {
headers: { 'content-type': 'text/html' },
});
}
사전 렌더링 기능 지원
📝서버 액션
서버
"use server"
import {PrismaClient} from "@prisma/client";
import {NextResponse} from "next/server";
/** 서버 컴포넌트에서 이용하는 서버액션 방식 (Back End) **/
export async function getUsers() {
const prisma = new PrismaClient()
const test = await prisma.test_table.findFirst();
console.log(test)
// return NextResponse.json(test);
return test;
}
/** useEffect 이용한 Data Fetch (Front End) **/
export async function GET(request: Request) {
const data = await getUsers();
console.log(data);
return NextResponse.json(data);
}
클라이언트
"use client"
import {getUsers} from "@/app/api/main/route";
import {useEffect, useState} from "react";
export default function Page () {
const [user, setUser] = useState<any>(null);
/** 서버 액션 방식 **/
// useEffect(() => {
// async function load() {
// const data = await getUsers();
// setUser(data);
// }
// load();
// }, []);
/** Fetch 방식 **/
useEffect(() => {
async function load() {
const res = await fetch('/api/main');
const data = await res.json();
setUser(data);
}
load();
}, []);
return <div>
{user?.column_name}
<button onClick={getUsers}>btn</button>
</div>
}
"use server”가 붙어있는 파일이다. 해당 파일에는 백엔드 관련 로직이 들어가고 클라이언트 컴포넌트에서 fetch를 통한 api 호출하는 것이 아닌 해당 함수를 직접 호출시켜서 사용한다.
📝forwardRef → ref로 변경
// 이전 코드 forwardRef
import { forwardRef } from 'react';
const MyInput = forwardRef<HTMLInputElement, { placeholder: string }>(
function MyInput({ placeholder }, ref) {
return <input ref={ref} placeholder={placeholder} />;
}
);
const inputRef = useRef<HTMLInputElement>(null);
<MyInput placeholder="이름" ref={inputRef} />;
// 현재 코드 re
function MyInput({ placeholder, ref }: { placeholder: string; ref?: React.Ref<HTMLInputElement> }) {
return <input ref={ref} placeholder={placeholder} />;
}
const inputRef = useRef<HTMLInputElement>(null);
<MyInput placeholder="이름" ref={inputRef} />;
상위 컴포넌트 참조 필요시 forwardRef에서 ref로 변경
ref 사용하는 곳
- DOM 직접 제어 (포커스 설정, 스크롤 이동, 텍스트 선택 등)
- 외부 라이브러리 사용 (D3, Chart.js처럼 직접 DOM에 접근해야 하는 경우)
📝ref 클린업 제공
<input
ref={(ref) => {
// ref created
// NEW: return a cleanup function to reset
// the ref when element is removed from DOM.
return () => {
// ref cleanup
};
}}
/>
컴포넌트가 언마운트 되면 ref 콜백에서 clean up 함수를 호출합니다.
📝React Helmet
function BlogPost({post}) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<meta name="keywords" content={post.keywords} />
<p>
Eee equals em-see-squared...
</p>
</article>
);
}
React Helmlet에서 사용하던 기능 제공
반응형