컴포넌트 (Component)란?
리액트는 화면에서 UI 요소를 구분할 때 '컴포넌트'라는 단위를 사용한다. 쉽게 말하면 리액트에서 앱을 이루는 가장 작은 조각이라고 할 수 있고, 레고 블록으로 집을 쌓게 된 경우 하나의 블록이 '컴포넌트'라고 할 수 있다.
반복적인 html 축약할때, 큰 페이지들, 자주변경되는 것들 때 사용하면 좋다// ProfileCard.tsx type TProfileCardProps = { backgroundImage: string; userImage: string; userName: string; instarId: string; onFollowClick: () => void; }; const ProfileCard = ({ backgroundImage, userImage, userName, instarId, onFollowClick, }: TProfileCardProps) => { return ( <> <article className="card"> <img className="prof-img" src={userImage} alt="profile-pic" /> <h3 className="alias">{instarId}</h3> <p className="username">{userName}</p> <button onClick={onFollowClick}>Follow</button> </div> </article> </> ); }; export default ProfileCard;
// App.tsx // login 상태일때, 아닐때 표현 import ProfileCard from "./ProfileCard"; const App = () => { const isLoggedin = true; return ( <> {isLoggedin && ( <ProfileCard userImage="https://images.pexels.com/photos/415829/pexels" userName="@sallytheramos" instarId="Sally Ramos" onFollowClick={() => alert("Followed")} /> )} </> ); }; export default App;
onChange
<input> <textarea> <select> 와 같은 폼(Form) 엘리먼트는 사용자의 입력값을 제어하는 데 사용한다 React 에서는 이러한 변경될 수 있는 입력값을 일반적으로 컴포넌트의 state 로 관리하고 업데이트한다. onChange 이벤트가 발생하면 e.target.value 를 통해 이벤트 객체에 담겨있는 input 값을 읽어올 수 있다.
// ex) function NameForm() { const [name, setName] = useState(""); const handleChange = (e) => { setName(e.target.value); } return ( <div> <input type="text" value={name} onChange={handleChange}></input> <h1>{name}</h1> </div> ) };
onClick
onClick 이벤트는 말 그대로 사용자가 클릭이라는 행동을 하였을 때 발생하는 이벤트이다. 버튼이나 <a> tag 를 통한 링크 이동 등과 같이 주로 사용자의 행동에 따라 애플리케이션이 반응해야 할 때 자주 사용한다. function NameForm() { const [name, setName] = useState(""); const handleChange = (e) => { setName(e.target.value); }; const handleClick = () => { alert(name); }; return ( <div> <input type="text" value={name} onChange={handleChange}></input> <button onClick={handleClick}>Button</button> <h1>{name}</h1> </div> ); };
React 컴포넌트에서 children은 부모 컴포넌트 사이에서 자식 컴포넌트를 가리키는 특별한 prop을 말한다. 여기서 중요한 점은, children prop은 명시적으로 전달하지 않아도 부모 컴포넌트 사이의 내용을 자동으로 전달받는다는 것이다. 이를 통해 컴포넌트가 재사용 가능한 '틀' 역할을 하도록 할 수 있다.
// children prop // RedColor.tsx const RedColor = ({ children }: { children: React.ReactNode }) => { return ( <> <div style={{ color: "red", fontSize: "50px" }}>{children}</div> </> ); }; export default RedColor;
// App.tsx import RedColor from "./RedColor"; const App = () => { return ( <> <RedColor> I <span style={{ textDecoration: "underline" }}>want</span> to Text </RedColor> </> ); }; export default App;
이런 방식으로 children prop을 사용하면, 컴포넌트를 재사용 가능한 '틀'처럼 사용할 수 있으며, 이 틀 안에 어떤 내용을 넣을지는 컴포넌트를 사용하는 측에서 결정할 수 있게 된다. 이는 코드의 재사용성을 높이고 유연성을 제공한다.
Props는 부모 컴포넌트가 자식 컴포넌트에게 전달하는 값이다.
Props는 객체여서 넘겨 받을때, 자식 컴포넌트에서 'props.이름'으로 접근할수 있다, 비구조화 할당도 가능하다// Greeting.tsx const Greeting = (props: { name: string; age: number }) => { return ( <> <h1> Hello, {props.name} {props.age} </h1> </> ); }; export default Greeting;
// Eat.tsx const Eat = (props: { name: string; age: number; onFinishEat: () => void }) => { return ( <> <h1> {props.name} - {props.age} 님이 식사를 시작합니다 </h1> <button onClick={props.onFinishEat}>식사완료</button> </> ); }; export default Eat;
Interface, Type 타입으로 지정해 받을 수 있다.
// interface는 type폴더에 따로 지정해 전역에서 사용할수있다. 앞에 I를 붙여주면 더 알아보기 쉽다 // index.d.ts export interface IGreetingProps = { name: string; age: number; }; // type은 적은 곳에서만 사용이 가능하며, 앞에 T를 붙여주면 더 알아보기 쉽다 type TGreetingProps = { name: string; age: number; }; // 받을때 interface는 import 해줘야한다. type폴더라는 곳에 따로 지정을 해줬으니 const Greeting = (props: IGreetingProps) => {} const Greeting = (props: TGreetingProps) => {}
button, input 같은 태그에 {...rest}로 props를 받아오면, rest 안에 있는 객체안에 있는 값들을 모두 button, input 등 태그에 설정을 해준다.
// Input.tsx type TInputProps = React.ComponentPropsWithoutRef<"input">; // html 태그이름이랑 속성명이 이름 똑같을때 똑같아야함 // React.ComponentPropsWithoutRef 사용시 const Input = ({ ...rest }: TInputProps) => { return ( <> <input className="w-[240px] h-[44px] rounded-lg placeholder::text-[#acacac] border border-[#4F4F4F] py-[13.5px] px-[16px] outline-none text-sm outline-none" {...rest} /> </> ); }; export default Input;
// App.tsx import Input from "./Input"; const App = () => { return ( <> <div className="flex justify-center items-center min-h-screen"> <Input type="text" placeholder="Enter Todo List" /> </div> </> ); }; export default App;
import { twMerge } from "tailwind-merge"; import { twJoin } from "tailwind-merge"; // twMerge 뒤에께 앞에걸 덮어씌움 // twJoin 앞에께 뒤에꺼를 덮음 const App = () => { // loggedIn = true -> text-5xl text-rose-500 // loggedIn = false -> text-3xl const isLoggedIn = true; return ( <> {/* <h1 className={isLoggedIn ? "text-5xl" : "text-3xl"}>aaa</h1> */} // twMerge로 변경 <h1 className={twMerge("text-3xl", isLoggedIn ? "text-5xl" : "")}>aaa</h1> </> ); }; export default App;