<div id="content">
<p>Hello world!</p>
</div>
React.createElement(Component Type, Props Object, Child Component)React.createElement(
'div',
{id:'content'},
React.createElement(
'p',
null,
'Hello world'
)
)
// index.jsx
import React from 'react';
// ReactDOM.createRoot(entryPoint).render(<App />);
ReactDOM.createRoot(entryPoint).render(React.createElement(App));
이런 식으로 고칠 수 있다!
// App.jsx
return(
<div>
<Header />
<main>
</main>
</div>
)
<div>와 관련된 설정값도 없어서 굳이 <div>를 써 놓을 필요가 없었다.<div>를 넣은 이유는 이것을 지우는 순간 오류가 발생하기 때문!<div id="root">
<div>
<header></header>
<main></main>
</div>
</div>
이러한 것들의 대안으로 나온 것이 Fragment
import {useState, Fragment} from 'react';
return (
<Fragment>
<Header />
<main>
</main>
</Fragment>
);
이렇게하면 다음과 같은 DOM 구조가 나온다.
<div id="root">
<header></header>
<main></main>
</div>
→ 최신에는 이렇게도 사용할 수 있다.
// import도 안해도 됨
return(
<>
<Header />
<main>...
</main>
</>
)
import Header from "./components/Header/Header.jsx";
import CoreConcepts from "./components/CoreConcepts.jsx";
import Examples from "./components/Examples.jsx";
function App() {
return (
<>
<Header />
<main>
<CoreConcepts />
<Examples />
</main>
</>
);
}
export default App;
import CoreConcept from "./CoreConcept.jsx";
import { CORE_CONCEPTS } from "../data";
export default function CoreConcepts() {
return (
<section id="core-concepts">
<h2>Core Concepts</h2>
<ul>
{CORE_CONCEPTS.map((conceptItem) => (
<CoreConcept key={conceptItem.title} {...conceptItem} />
))}
</ul>
</section>
);
}
import { useState } from "react";
import TabButton from "./TabButton.jsx";
import { EXAMPLES } from "../data.js";
export default function Examples() {
const [selectedTopic, setSelectedTopic] = useState();
function handleSelect(selectedButton) {
// selectedButton => 'components', 'jsx', 'props', 'state'
setSelectedTopic(selectedButton);
}
let tabContent = <p>Please select a topic.</p>;
if (selectedTopic) {
tabContent = (
<div id="tab-content">
<h3>{EXAMPLES[selectedTopic].title}</h3>
<p>{EXAMPLES[selectedTopic].description}</p>
<pre>
<code>{EXAMPLES[selectedTopic].code}</code>
</pre>
</div>
);
}
return (
<section id="examples">
...
</section>
);
}
🚨분리할 때, jsx 부분을 return으로 감싸는 것 잊지 않기!🚨
// CoreConcepts.jsx
export default function CoreConcepts() {
return (
<section id="core-concepts">
<h2>Core Concepts</h2>
<ul>
</ul>
</section>
);
}
// Examples.jsx
export default function Examples() {
return(
<section id="examples">
<h2>Examples</h2>
<menu>
</menu>
</section>
);
}
<section>-제목-내용 순으로 이뤄져 있다. 이것을 이용해 Section.jsx 컴포넌트를 생성// Section.jsx
export default function Section({ title, id, children }) {
return (
<section id={id}>
<h2>{title}</h2>
{children}
</section>
);
}
<section id="example">과 같은 prop들은 커스텀 컴포넌트에 설정할 때 자동으로 적용되거나 해당 컴포넌트 속 JSX 코드로 넘어가지 않는다. Props are not forwarded automatically.Section({id})를 이용하여 속성값을 전달했다.🚨 하지만 이런 방식을 사용한다면 개발자는 속성을 계속해서 설정을 해야한다..! → 비효율적 🚨
따라서 forwarded props(전달 속성), proxy props(대리 속성)을 사용한다.
export default function Section({ title, children, ...props }){
return (
<section {...props}>
<h2>{title}</h2>
{children}
</section>
);
}
Section({...props}) : 자바스크립트의 내장문법. title과 children을 제외한 모든 다른 props를 모아서 props object로 병합한다. 이 경우에는 데이터를 객체로 모으기 위해 사용.<section {...props}> : 데이터, 즉 값의 집합을 펼쳐서 다른 요소에 보내기 위함.이렇게하면 개발자가 직접 속성을 설정하는 등의 비효율적인 요소가 줄어들게 된다!
// TabButton.jsx
export default function TabButton({ children, isSelected, ...props }) {
console.log("TABBUTTON COMPONENT EXECUTING");
return (
<li>
<button className={isSelected ? "active" : undefined} {...props}>
{children}
</button>
</li>
);
}
// Examples.jsx
<TabButton
isSelected={selectedTopic === "components"}
onClick={() => handleSelect("components")}
>
{...props}를 이용하여 onClick 동작시킨다.export default function Tabs({ children, buttons }) {
return (
<>
<menu>{buttons}</menu>
{children}
</>
);
}
export default function Examples(){
return (
<Section id="examples" title="Examples">
<Tabs
buttons={
<>
<TabButton
isSelected={selectedTopic === "components"}
onClick={() => handleSelect("components")}
>
Components
</TabButton>
<TabButton
isSelected={selectedTopic === "jsx"}
onClick={() => handleSelect("jsx")}
>
JSX
</TabButton>
<TabButton
isSelected={selectedTopic === "props"}
onClick={() => handleSelect("props")}
>
Props
</TabButton>
<TabButton
isSelected={selectedTopic === "state"}
onClick={() => handleSelect("state")}
>
State
</TabButton>
</>
}
>
{tabContent}
</Tabs>
</Section>
);
}
buttons라는 props를 생성한 뒤, 해당 props안에 넣고자하는 모든 버튼들을 넣는다. 이때, 하나의 루트를 통해서 전달하는 것처럼 fragment로 감싼 뒤, 버튼을 전달한다.children)도 전달하여 결과값인 tabContent를 표현하게끔 한다.Tabs)에 속해있기 때문에 다양한 HTML 요소를 동적으로 렌더링할 수 있다.// Tabs.jsx
export default function Tabs({ children, buttons, buttonsContainer }) {
const ButtonsContainer = buttonsContainer; // 커스텀 컴포넌트로서 사용되서 대문자로 시작.
return (
<>
<ButtonsContainer>{buttons}</ButtonsContainer>
{children}
</>
);
}
// Examples.jsx
<Tabs
// buttonsContainer={Section}
buttonsContainer="menu"
buttons={}>...</Tabs>
buttonsContainer={Section} : 커스텀 컴포넌트는 동적값으로 설정. 내장요소는 단순히 문자열로 전달.// Tabs.jsx
export default function Tabs({ children, buttons, ButtonsContainer }) {
return (
<>
<ButtonsContainer>{buttons}</ButtonsContainer>
{children}
</>
);
}
// Examples.jsx
<Tabs
ButtonsContainer="menu"
></Tabs>
- 속성(
ButtonsContainer)이 반드시 받는 쪽 컴포넌트(Tabs)에서 커스텀 컴포넌트로서 사용 가능해야 한다.- 식별자에는 문자열 이름(
menu, ul, div..)을 사용하는데 만약 커스텀 컴포넌트({Section})를 사용하고 싶다면 컴포넌트 함수를 사용해야만 적용 가능하다.
// Tabs.jsx
export default function Tabs({ children, buttons, ButtonsContainer = "menu" }) {
// const ButtonsContainer = buttonsContainer; // 커스텀 컴포넌트로서 사용되서 대문자로 시작.
return (
<>
<ButtonsContainer>{buttons}</ButtonsContainer>
{children}
</>
);
}
// Examples.jsx
<Tabs buttons={}>
</Tabs>
<Tabs ButtonsContainer="menu" buttons={}>에서 ButtonsContainer 속성을 삭제했다.