728x90
반응형
기본 개념
Controlled Components
- 사용자 입력을 컴포넌트의 state로 관리하는 컴포넌트
- UI에서 입력한 데이터와 저장한 데이터의 상태가 항상 일치
- 변경할 수 있는 state는 일반적으로 컴포넌트의 state 속성에 유지되고 setState()에 의해 업데이트
- 사용자가 입력할 때마다 리렌더링되므로, 실시간으로 값이 필요한 경우 사용
function ControlledInput() {
const [value, setValue] = React.useState("");
return (
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
);
}
Uncontrolled Components
- React가 아닌 DOM 자체에서 상태를 관리하는 컴포넌트
- 입력값을 제어하지 않고, 필요할 때 ref를 사용해 값을 가져와 사용
- 기존의 바닐라 js와 유사하며, form을 제출할 때 실행되는 함수에서 값을 얻어올 수 있음
- 불필요한 리렌더링을 줄이고, 특정 시점에만 값을 사용할 수 있음
function UncontrolledInput() {
const inputRef = React.useRef();
const handleSubmit = () => {
console.log(inputRef.current.value);
};
return <input type="text" ref={inputRef} />;
}
적용하기 - Search bar
Controlled Component
- 사용자 입력에 따라 실시간으로 URL 업데이트
- debouncing을 사용해 사용자 입력값이 변경된 후 일정 시간 경과 후 URL 업데이트
"use client";
import { cn } from "../utils/style";
import { Input } from "../ui/input";
import { SearchIcon } from "lucide-react";
import { useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useDebouncedCallback } from "use-debounce";
export type TypeSearchbarProps = {
placeholder: string;
iconPosition: "left" | "right";
wrapperClassName?: string;
};
export const Searchbar = ({
placeholder,
iconPosition,
wrapperClassName,
}: TypeSearchbarProps) => {
const searchParams = useSearchParams();
const keyword = searchParams.get("keyword") || "";
const pathname = usePathname();
const router = useRouter();
// 입력값을 useState로 관리하여 React state와 동기화
const [search, setSearch] = useState(keyword);
// 디바운싱된 URL 업데이트 함수
const updateURL = useDebouncedCallback((value: string) => {
const params = new URLSearchParams();
if (value) {
params.set("keyword", value);
}
router.push(`${pathname}?${params.toString()}`);
}, 300);
// 입력값 변경 핸들러
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setSearch(value); // React state 업데이트
updateURL(value); // 디바운싱된 URL 업데이트 호출
};
return (
<div
className={cn(
`flex items-center gap-2 px-2 border border-gray-200 rounded-md w-full has-[:focus]:border-gray-500`,
iconPosition === "right" && "flex-row-reverse",
wrapperClassName
)}
>
<label htmlFor="search">
<SearchIcon className="w-4 h-4 text-gray-500" />
</label>
<Input
id="search"
type="text"
placeholder={placeholder}
value={search} // Controlled Component: React state와 연결
onChange={handleInputChange} // 입력값 변경 핸들러
className="border-none shadow-none focus:outline-none focus:border-transparent"
style={{
boxShadow: "none",
}}
/>
</div>
);
};
Uncontrolled Component
- 사용자 입력 후 'Enter key'를 눌렀을 때 URL 변경
- 상태를 관리하지 않으므로
"use client";
import { cn } from "../utils/style";
import { Input } from "../ui/input";
import { SearchIcon } from "lucide-react";
import { useRef } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
export type TypeSearchbarProps = {
placeholder: string;
iconPosition: "left" | "right";
wrapperClassName?: string;
};
export const Searchbar = ({
placeholder,
iconPosition,
wrapperClassName,
}: TypeSearchbarProps) => {
const searchbarRef = useRef<HTMLInputElement>(null);
const searchParams = useSearchParams();
const keyword = searchParams.get("keyword") || "";
const pathname = usePathname();
const router = useRouter();
const handleSearch = (e: React.KeyboardEvent<HTMLInputElement>) => {
const search = searchbarRef.current?.value;
if (search && e.key === "Enter") {
const params = new URLSearchParams();
params.set("keyword", search);
router.push(`${pathname}?${params.toString()}`);
}
return;
};
return (
<div
className={cn(
`flex items-center gap-2 px-2 border border-gray-200 rounded-md w-full has-[:focus]:border-gray-500,`,
iconPosition === "right" && "flex-row-reverse",
wrapperClassName
)}
>
<label htmlFor="search">
<SearchIcon className="w-4 h-4 text-gray-500" />
</label>
<Input
ref={searchbarRef} // 값 참조
id="search"
type="text"
placeholder={placeholder}
defaultValue={keyword} // 기본값 추가
className="border-none shadow-none focus:outline-none focus:border-transparent"
style={{
boxShadow: "none",
}}
onKeyDown={handleSearch}
/>
</div>
);
};
728x90
반응형
'개발 공부' 카테고리의 다른 글
주식 투자와 AI의 만남: Global Stock Analyst GPTs 개발기 (0) | 2024.12.08 |
---|---|
Chrome 확장 프로그램 - JSON Viewer (0) | 2023.02.16 |
AWS 청구서 (0) | 2023.02.06 |