React.cloneElement를 어디다 사용하는지 알아봅니다.
해당 글의 번역입니다 : https://blog.logrocket.com/using-react-cloneelement-function/
React.cloneElement()가 뭐죠?
- 원래 엘리먼트의 props와 새 props가 얕게 병합됨
- 새 child가 기존 child를 대체합니다.
- 원래 엘리먼트의 key와 ref가 유지됩니다.
React.cloneElement()는 불필요한 중복 코드를 피하면서
상위 컴포넌트의 하위 요소를 추가하거나 수정하려는 경우에 유용합니다.
React.cloneElement()의 구문과 사용
React.cloneElement(element, [props], [...children])
- element: 복제할 엘리먼트
- [props]: 원본 엘리먼트에 추가하여 복제된 엘리먼트에 추가될 props
- [...children]: 복제된 객체의 children입니다. 기존 객체의 자식은 복사되지 않습니다.
- children props 수정
- children props 추가
- children 컴포넌트의 기능 확장
children props 수정
예제에서 수정된 props가 얕게 병합된다는 점에 유의하십시오.
key와 ref는 유지됩니다.
1. 반복되는 문자열
다음 코드 블록에서 RepeatCharacters는 상위 컴포넌트이고
CreateTextWithProps는 하위 컴포넌트 입니다.
CreateTextWithProps에는 값이 유효한 ASCII 문자인 ASCIIChar라는 prop이 있습니다.
RepeatCharacters는 React.cloneElement()를 사용하여 times 속성에 지정된 횟수만큼 복제된 엘리먼트에서 이 문자를 반복합니다.
import React from "react";
const CreateTextWithProps = ({ text, ASCIIChar, ...props }) => {
return (
<span {...props}>
{text}{ASCIIChar}
</span>
)
};
const RepeatCharacters = ({ times, children }) => {
return React.cloneElement(children, {
// This will override the original ASCIIChar in the text.
ASCIIChar: children.props.ASCIIChar.repeat(times),
})
};
function App() {
return (
<div>
<RepeatCharacters times={3}>
<CreateTextWithProps
text="Habdul Hazeez"
ASCIIChar='.'
/>
</RepeatCharacters>
</div>
)
}
export default App
2. 멋진 children 버튼
"Fancy button"이라는 텍스트가 있는 버튼을 보여주는 코드입니다.
ButtonContainer 컴포넌트는
React.cloneElement()를 사용하여 Button 컴포넌트에 의해 렌더링된 엘리먼트의 모양을 수정합니다.
import React from "react";
const ButtonContainer = (props) => {
let newProp = {
backgroundColor: "#1560bd",
textColor: '#ffffff',
border: '1px solid #cccccc',
padding: '0.2em',
}
return (
<div>
{React.Children.map(props.children, child => {
return React.cloneElement(child, {newProp}, null)
})}
</div>
)
};
const Button = (props) => {
return <button
style={{
color: props.newProp.textColor,
border: props.newProp.border,
padding: props.newProp.padding,
backgroundColor: props.newProp.backgroundColor
}}>Fancy Button</button>
}
function App() {
return (
<ButtonContainer>
<Button />
</ButtonContainer>
)
}
export default App
3. 라디오 버튼 속성 수정
HTML에서 라디오 버튼은 그룹화됩니다.
제공된 옵션 중 하나만 선택할 수 있으며
모든 컴포넌트에 항상 동일한 name prop이 첨부되어 있는 것 같습니다.
지금까지 얻은 지식으로 단일 상위 컴포넌트를 통해
이 name 속성을 여러 하위 컴포넌트에 동적으로 추가할 수 있습니다.
import React from "react";
const RadioGroup = (props) => {
const RenderChildren = () => (
React.Children.map(props.children, child => {
return React.cloneElement(child, {
name: props.name,
})
})
)
return (
<div>
{<RenderChildren />}
</div>
)
}
const RadioButton = (props) => {
return (
<label>
<input type="radio" value={props.value} name={props.name} />
{props.children}
</label>
)
}
function App() {
return (
<RadioGroup name="numbers">
<RadioButton value="first">First</RadioButton>
<RadioButton value="second">Second</RadioButton>
<RadioButton value="third">Third</RadioButton>
</RadioGroup>
)
}
export default App
4. 다른 React 요소를 Prop으로 복제
다른 웹 페이지에 대해 다른 텍스트로 웹 사이트 헤더를 만들어야 하는 상황에 처하게 될 것입니다.대신 React.cloneElement를 사용하여 동일한 목표를 더 쉽게 달성할 수 있습니다.
다음과 같이 재사용 가능한 세 가지 컴포넌트를 만듭니다.
- Header
- DefaultHeader
- BigHeader
Header 컴포넌트는 컴포넌트를 prop으로 받습니다.
이 컴포넌트는 헤더 텍스트를 렌더링합니다.
DefaultHeader는 Header에 전달되는 기본 컴포넌트입니다.
DefaultHeader는 기본 텍스트를 렌더링합니다.
기본 텍스트는 props 없이 Header가 호출될 때 렌더링됩니다.
한편 BigHeader 컴포넌트에는 값이 선택한 헤더 텍스트인 메시지 prop이 있습니다.
BigHeader를 Header에 전달할 때마다
BigHeader에 의해 렌더링되기 전에 이 메시지 prop의 값을 수정할 수 있습니다.
이 모든 것이 다음 코드 블록에 설명되어 있습니다.
import React from "react";
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
const DefaultHeader = (color) => {
return (
<div style={{ color: "#1560bd" }}>
<p>Website of Habdul Hazeez</p>
</div>
)
}
const defaultMessage = 'Website of Habdul Hazeez';
const BigHeader = ({ color, message = defaultMessage }) => {
return (
<div style={{ color, fontSize: '2em' }}>
{message}
</div>
)
}
const Header = ({ hero = <DefaultHeader />}) => {
return (
<div>
{React.cloneElement(hero, { color: "#1560bd"})}
</div>
)
}
const HomePage = () => {
return (
<Header hero={<BigHeader message="This is the home page" />} />
)
}
const AboutMe = () => {
return (
<Header hero={<BigHeader message="Information about me" />} />
)
}
const ContactPage = () => {
return (
<Header hero={<BigHeader message="This contains my contact information." />} />
)
}
function App() {
return (
<React.Fragment>
<Router>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/contact-page">Contact</Link>
</li>
<li>
<Link to="about-me">About</Link>
</li>
</ul>
</nav>
<Route exact path="/"><HomePage /></Route>
<Route path="/contact-page"><ContactPage /></Route>
<Route path="/about-me"><AboutMe /></Route>
</Router>
</React.Fragment>
)
}
export default App
Children에 프롭 추가하기
부모 컴포넌트를 통해 자식에 새로운 속성을 삽입한다는 의미입니다.
이 섹션에서 논의되는 다음 예제는 React.cloneElement()를 통해 props를 추가하는 방법을 설명합니다.
이전 섹션에서 언급했듯이 새 props가 병합되고 key와 ref가 유지되며 새 children이 생성되지 않습니다.
1. 두꺼운 텍스트
React.cloneElement() 함수를 사용하여 child에 추가되는 CSS 스타일을 정의합니다.
import React from "react";
const CreateTextWithProps = ({ text, ...props }) => {
return (
<span {...props}>
{text}
</span>
)
};
const BoldText = ({ children }) => {
return React.cloneElement(children, {
style: {
fontWeight: 'bold'
},
})
};
function App() {
return (
<div>
<BoldText>
<CreateTextWithProps
text="Habdul Hazeez"
/>
</BoldText>
</div>
)
}
export default App;
2. React.cloneElement()를 통해 prop으로 전달받은 Element에 prop 전달하기
React.cloneElement()를 사용하여 prop을 복제합니다.
이 prop은 <h1> 또는 <button>과 같은 유효한 React 엘리먼트여야 합니다.
프롭을 복제할 때 CSS 스타일 또는 이벤트 핸들러와 같은 추가 속성을 전달할 수 있습니다.
import React from "react";
const AlertOnClick = (props) => {
function handleClick () {
alert("Hello World")
}
const Trigger = props.trigger;
return (
<div>
{React.cloneElement(Trigger, {
onClick: handleClick
})}
</div>
)
}
function App() {
return (
<div>
{
<AlertOnClick trigger={<button>Click me</button>} />
}
</div>
)
}
export default App
자식 컴포넌트의 기능 확장하기
지금까지 children prop을 수정하고 추가했습니다.
마침내 새로운 children을 생성합니다.
복제된 요소의 마지막 특성을 보여줍니다!
이 새 자식은 React.cloneElement()의 세 번째 인수로 전달됩니다.
Alert on click
이 예에서 우리는 자식의 기능을 확장할 것입니다.
다음 코드 블록에서 Button 구성 요소의 React.cloneElement()를 통해 생성된
새 버튼 요소에는 onClick 이벤트 핸들러와 새 텍스트라는 두 가지 추가 기능이 있습니다.
import React from "react";
const Button = () => {
return (
<button type="button" style={{ padding: "10px" }}>
This is a styled button
</button>
)
}
function App() {
return (
<section>
{
React.cloneElement(
Button(), // component to overwrite
{
onClick: () => { // additional props
alert("You are making progress!!!")
}
},
<>
Styled button with onClick
</>
)
}
</section>
)
}
export default App
이 코드가 실행되면 버튼은 React.cloneElement() 함수를 통해 추가된 텍스트를 이용해 렌더링되며
클릭 시 얼럿을 표시합니다.
(button의 children을 대체하기 때문임.)
더 보기
https://blog.cristiana.tech/react-children-map-and-cloneelement-using-typescript
https://stackoverflow.com/questions/58123398/when-to-use-jsx-element-vs-reactnode-vs-reactelement
'FrontEnd' 카테고리의 다른 글
소프트웨어 합성 : 리듀서(reducer) (0) | 2022.09.20 |
---|---|
AST 활용 1편 : ESLint console.log 체크 플러그인 만들기 (0) | 2022.09.19 |
리액트 디자인 패턴 : 컴파운드 컴포넌트 패턴 [Compound Component Pattern] 2 (0) | 2022.09.15 |
[Babel] 바벨 플러그인을 작성하며 AST 배우기 (0) | 2022.09.14 |
[typescript] d.ts 파일을 js 프로젝트에서 사용할 수 있을까? (3) | 2022.09.14 |