cloneElement
cloneElement γδ½Ώη¨γγγ¨γε₯οΏ½?θ¦η΄ γ«εΊγ₯γγ¦ζ°γγ React θ¦η΄ γδ½ζγγγγ¨γγ§γγΎγγ
const clonedElement = cloneElement(element, props, ...children)γͺγγ‘γ¬γ³γΉ
cloneElement(element, props, ...children)
cloneElement γεΌγ³εΊγγ¦γelement γεΊγ«γη°γͺγ props γ¨ children γζγ£γ React θ¦η΄ γδ½ζγγΎγγ
import { cloneElement } from 'react';
// ...
const clonedElement = cloneElement(
<Row title="Cabbage">
Hello
</Row>,
{ isHighlighted: true },
'Goodbye'
);
console.log(clonedElement); // <Row title="Cabbage" isHighlighted={true}>Goodbye</Row>εΌζ°
-
element:elementεΌζ°γ―ζεΉγͺ React θ¦η΄ γ§γͺγγγ°γͺγγΎγγγδΎγγ°γ<Something />οΏ½?γγγͺ JSX γγΌγγcreateElementοΏ½?εΌγ³εΊγη΅ζγγΎγγ―ε₯οΏ½?cloneElementοΏ½?εΌγ³εΊγη΅ζγͺγ©γ§γγ -
props:propsεΌζ°γ―γͺγγΈγ§γ―γγnullγ§γͺγγγ°γͺγγΎγγγnullγζΈ‘γγ¨γγ―γγΌγ³γγγθ¦η΄ γ―ε οΏ½?element.propsγγγΉγ¦δΏζγγΎγγγγδ»₯ε€οΏ½?ε ΄εγpropsγͺγγΈγ§γ―γε οΏ½?γγΉγ¦οΏ½?ι οΏ½?γ«γ€γγ¦γθΏγγγθ¦η΄ γ§γ―element.propsοΏ½?ε€γγγpropsγγοΏ½?ε€γγεͺε γγγγΎγγοΏ½?οΏ½γοΏ½? props γ―ε οΏ½?element.propsγγεγγγγΎγγprops.keyγprops.refγζΈ‘γγε ΄εγγγγγ―ε οΏ½?γοΏ½?γοΏ½?γζγγΎγγ -
ηη₯ε―θ½
...children: γΌγεδ»₯δΈοΏ½?εγγΌγγγγγγ React γγΌγγγ€γΎγ React θ¦η΄ γζεεγζ°ε€γγγΌγΏγ«γη©ΊγγΌγοΌnullγundefinedγtrueγfalseοΌγReact γγΌγοΏ½?ι εγ«γͺγγΎγγ...childrenεΌζ°γζΈ‘γγͺγε ΄εγε οΏ½?element.props.childrenγδΏζγγγΎγγ
θΏγε€
cloneElement γ―δ»₯δΈοΏ½?γγγγγ£γζγ€ React θ¦η΄ γͺγγΈγ§γ―γγθΏγγΎγγ
type:element.typeγ¨εγγprops:element.propsγ«γζΈ‘γγγδΈζΈγη¨οΏ½?propsγζ΅ γγγΌγΈγγη΅ζγref: ε οΏ½?element.refγγγ γγprops.refγ«γγ£γ¦δΈζΈγγγγε ΄εγ―ι€γγkey: ε οΏ½?element.keyγγγ γγprops.keyγ«γγ£γ¦δΈζΈγγγγε ΄εγ―ι€γγ
ιεΈΈγγοΏ½?θ¦η΄ γγ³γ³γγΌγγ³γγγθΏγγγδ»οΏ½?θ¦η΄ οΏ½?εγ¨γγ¦η¨γγΎγγθ¦η΄ οΏ½?γγγγγ£γθͺγΏεγγγ¨γ―ε―θ½γ§γγγδ½ζεΎγ―θ¦η΄ οΏ½?ζ§ι γιε ¬ι (opaque) γ¨γγ¦ζ±γγγ¬γ³γγΌοΏ½?γΏθ‘γγγγ«γγγΉγγ§γγ
注ζηΉ
-
θ¦η΄ γγ―γγΌγ³γγ¦γε οΏ½?θ¦η΄ γ―ε€ζ΄γγγΎγγγ
-
θ€ζ°οΏ½?εοΏ½?ε οΏ½?οΏ½γγγΉγ¦ιηγ«εγγ£γ¦γγε ΄εγ
cloneElementγ«γ―εγcloneElement(element, null, child1, child2, child3)οΏ½?γγγ«θ€ζ°οΏ½?εΌζ°γ¨γγ¦ζΈ‘γγ¦γγ γγγεγεηγͺε ΄εγ―γι εε ¨δ½γ第 3 εΌζ°γ¨γγ¦cloneElement(element, null, listItems)οΏ½?γγγ«ζΈ‘γγ¦γγ γγγγγγ«γγγReact γ―εηγͺγͺγΉγγ«keyγζ¬ γγ¦γγε ΄εγ«θ¦εγεΊγγγγ«γͺγγΎγγιηγͺγͺγΉγγ§γ―δΈ¦γ³ζΏγγ―ζ±Ίγγ¦ηΊηγγͺγγγγkey γ―εΏ θ¦γγγΎγγγ -
cloneElementγδ½Ώγγ¨γγΌγΏγγγΌοΏ½?θΏ½θ·‘γι£γγγͺγγγγ代γγγ«δ»£ζΏζοΏ½?οΏ½γ試γγ¦γΏγ¦γγ γγγ
δ½Ώη¨ζ³
θ¦η΄ οΏ½? props γδΈζΈγγγ
React θ¦η΄ οΏ½? props γδΈζΈγγγγ«γ―γγγγ cloneElement γ«ζΈ‘γγδΈζΈγγγγ props γζοΏ½?οΏ½γγΎγγ
import { cloneElement } from 'react';
// ...
const clonedElement = cloneElement(
<Row title="Cabbage" />,
{ isHighlighted: true }
);γοΏ½?ε ΄εγη΅ζγ¨γͺγγ―γγΌγ³γγγθ¦η΄ γ― <Row title="Cabbage" isHighlighted={true} /> γ«γͺγγΎγγ
δΎγδ½Ώγ£γ¦γγγγε½Ήη«γ€ε ΄ι’γθ¦γ¦γΏγΎγγγγ
ιΈζε―θ½γͺθ‘οΏ½?γͺγΉγγ¨γιΈζγγγ¦γγθ‘γε€ζ΄γγ βNextβ γγΏγ³γγ¬γ³γγΌγγ List γ³γ³γγΌγγ³γγζ³εγγ¦γΏγ¦γγ γγγList γ³γ³γγΌγγ³γγ―γιΈζγγγ Row γη°γͺγζΉζ³γ§γ¬γ³γγΌγγεΏ
θ¦γγγγγγεγεγ£γγγΉγ¦οΏ½? <Row> γγ―γγΌγ³γγisHighlighted: true γΎγγ― isHighlighted: false γθΏ½ε οΏ½? props γ¨γγ¦ζοΏ½?οΏ½γγΎγγ
export default function List({ children }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{Children.map(children, (child, index) =>
cloneElement(child, {
isHighlighted: index === selectedIndex
})
)}δΎγγ° List γεγεγε
οΏ½? JSX γδ»₯δΈοΏ½?γγγͺγοΏ½?γ§γγε ΄εγθγγΎγγ
<List>
<Row title="Cabbage" />
<Row title="Garlic" />
<Row title="Apple" />
</List>εθ¦η΄ γγ―γγΌγ³γγγγ¨γ§γList γ―ε
ι¨οΏ½?γγΉγ¦οΏ½? Row γ«θΏ½ε ζ
ε ±γζΈ‘γγγ¨γγ§γγΎγγη΅ζγ―δ»₯δΈοΏ½?γγγ«γͺγγΎγγ
<List>
<Row
title="Cabbage"
isHighlighted={true}
/>
<Row
title="Garlic"
isHighlighted={false}
/>
<Row
title="Apple"
isHighlighted={false}
/>
</List>βNextβ γζΌγγ¨ List οΏ½? state γζ΄ζ°γγγη°γͺγθ‘γγγ€γ©γ€γγγγγγ¨γ«ηοΏ½?γγ¦γγ γγγ
import { Children, cloneElement, useState } from 'react'; export default function List({ children }) { const [selectedIndex, setSelectedIndex] = useState(0); return ( <div className="List"> {Children.map(children, (child, index) => cloneElement(child, { isHighlighted: index === selectedIndex }) )} <hr /> <button onClick={() => { setSelectedIndex(i => (i + 1) % Children.count(children) ); }}> Next </button> </div> ); }
γγγγγγγ¨γList γ―εγεγ£γ <Row /> θ¦η΄ γγ―γγΌγ³γγγγγγ«θΏ½ε οΏ½? props γδ»ε γγγ¨γγγγ¨γ§γγ
代ζΏζοΏ½?οΏ½
γ¬γ³γγΌγγγγγη¨γγ¦γγΌγΏγζΈ‘γ
cloneElement γδ½Ώη¨γγ代γγγ«γrenderItem οΏ½?γγγͺγ¬γ³γγΌγγγγ (render prop) γεγεγγγγ«γγγγ¨γζ€θ¨γγ¦γΏγ¦γγ γγγδ»₯δΈοΏ½?δΎγ§γ―γList γ― renderItem γ props γ¨γγ¦εγεγγΎγγList γ―εγ’γ€γγ γ«ε―Ύγγ¦ renderItem γεΌγ³εΊγγisHighlighted γεΌζ°γ¨γγ¦ζΈ‘γγΎγγ
export default function List({ items, renderItem }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{items.map((item, index) => {
const isHighlighted = index === selectedIndex;
return renderItem(item, isHighlighted);
})}renderItem οΏ½?γγγͺγοΏ½?γ―γγ¬γ³γγΌγγγγγγ¨εΌγ°γγΎγγδ½γγγ¬γ³γγΌγγζΉζ³γζοΏ½?οΏ½γγγγοΏ½? props γ γγγ§γγδΎγγ°γδΈγγγγ isHighlighted οΏ½?ε€γ§ <Row> γγ¬γ³γγΌγγ renderItem οΏ½?οΏ½?οΏ½θ£
γζΈ‘γγγ¨γγ§γγΎγγ
<List
items={products}
renderItem={(product, isHighlighted) =>
<Row
key={product.id}
title={product.title}
isHighlighted={isHighlighted}
/>
}
/>ζη΅ηγͺη΅ζγ― cloneElement γ¨εγγ§γγ
<List>
<Row
title="Cabbage"
isHighlighted={true}
/>
<Row
title="Garlic"
isHighlighted={false}
/>
<Row
title="Apple"
isHighlighted={false}
/>
</List>γγγγisHighlighted ε€γγ©γγγζ₯γ¦γγγγζη’Ίγ«θΏ½θ·‘γγγγ¨γγ§γγΎγγ
import { useState } from 'react'; export default function List({ items, renderItem }) { const [selectedIndex, setSelectedIndex] = useState(0); return ( <div className="List"> {items.map((item, index) => { const isHighlighted = index === selectedIndex; return renderItem(item, isHighlighted); })} <hr /> <button onClick={() => { setSelectedIndex(i => (i + 1) % items.length ); }}> Next </button> </div> ); }
γοΏ½?γγΏγΌγ³γ―γγζη€Ίηγ§γγγγγcloneElement γγγζ¨ε₯¨γγγΎγγ
γ³γ³γγ―γΉγγ§γγΌγΏγζΈ‘γ
cloneElement οΏ½?ε₯οΏ½?代ζΏζοΏ½?οΏ½γ¨γγ¦γ³γ³γγ―γΉγγιγγ¦γγΌγΏγζΈ‘γγγ¨γε―θ½γ§γγ
δΎγ¨γγ¦γcreateContext γεΌγ³εΊγγ¦ HighlightContext γοΏ½?οΏ½ηΎ©γγΎγγγγ
export const HighlightContext = createContext(false);List γ³γ³γγΌγγ³γγ―γγ¬γ³γγΌγγγγΉγ¦οΏ½?γ’γ€γγ γ HighlightContext γγγγ€γγ§γ©γγγγΎγγ
export default function List({ items, renderItem }) {
const [selectedIndex, setSelectedIndex] = useState(0);
return (
<div className="List">
{items.map((item, index) => {
const isHighlighted = index === selectedIndex;
return (
<HighlightContext.Provider key={item.id} value={isHighlighted}>
{renderItem(item)}
</HighlightContext.Provider>
);
})}γοΏ½?γ’γγγΌγγ§γ―γRow γ― props γ§ isHighlighted γεγεγεΏ
θ¦γδΈεγγγΎγγγ代γγγ«γ³γ³γγ―γΉγγγθͺγΏεγγΎγγ
export default function Row({ title }) {
const isHighlighted = useContext(HighlightContext);
// ...γγγ«γγγεΌγ³εΊγε
οΏ½?γ³γ³γγΌγγ³γγ― <Row> γ« isHighlighted γζΈ‘γγγ¨γ«γ€γγ¦η₯γεΏ
θ¦γγζ°γ«γγεΏ
θ¦γγͺγγͺγγΎγγ
<List
items={products}
renderItem={product =>
<Row title={product.title} />
}
/>代γγγ«γList γ¨ Row γ―γ³γ³γγ―γΉγγιγγγγ€γ©γ€γοΏ½?γγΈγγ―γ«ι’γγ¦εθͺΏγγ¦εδ½γγΎγγ
import { useState } from 'react'; import { HighlightContext } from './HighlightContext.js'; export default function List({ items, renderItem }) { const [selectedIndex, setSelectedIndex] = useState(0); return ( <div className="List"> {items.map((item, index) => { const isHighlighted = index === selectedIndex; return ( <HighlightContext.Provider key={item.id} value={isHighlighted} > {renderItem(item)} </HighlightContext.Provider> ); })} <hr /> <button onClick={() => { setSelectedIndex(i => (i + 1) % items.length ); }}> Next </button> </div> ); }
γ³γ³γγ―γΉγγιγγ¦γγΌγΏγζ·±γζΈ‘γζΉζ³γ«γ€γγ¦θ©³γγε¦γΆ
γγΈγγ―γγ«γΉγΏγ γγγ―γ«ζ½εΊγγ
試γγΉγε₯οΏ½?γ’γγγΌγγ―γγιθ¦θ¦ηγγͺγγΈγγ―γθͺεοΏ½?γγγ―γ«ζ½εΊγγγγγ―γγθΏγγγζ
ε ±γδ½Ώη¨γγ¦δ½γγ¬γ³γγΌγγγγζ±ΊοΏ½?οΏ½γγγγ¨γ§γγδΎγγ°ζ¬‘οΏ½?γγγͺ useList γ«γΉγΏγ γγγ―γζΈγγγ¨γγ§γγΎγγ
import { useState } from 'react';
export default function useList(items) {
const [selectedIndex, setSelectedIndex] = useState(0);
function onNext() {
setSelectedIndex(i =>
(i + 1) % items.length
);
}
const selected = items[selectedIndex];
return [selected, onNext];
}γγγδ»₯δΈοΏ½?γγγ«δ½Ώη¨γ§γγΎγγ
export default function App() {
const [selected, onNext] = useList(products);
return (
<div className="List">
{products.map(product =>
<Row
key={product.id}
title={product.title}
isHighlighted={selected === product}
/>
)}
<hr />
<button onClick={onNext}>
Next
</button>
</div>
);
}γγΌγΏγγγΌγ―ζη€Ίηγ§γγγstate γ―δ»»ζοΏ½?γ³γ³γγΌγγ³γγγδ½Ώη¨γ§γγ useList γ«γΉγΏγ γγγ―ε
γ«γγγΎγγ
import Row from './Row.js'; import useList from './useList.js'; import { products } from './data.js'; export default function App() { const [selected, onNext] = useList(products); return ( <div className="List"> {products.map(product => <Row key={product.id} title={product.title} isHighlighted={selected === product} /> )} <hr /> <button onClick={onNext}> Next </button> </div> ); }
γοΏ½?γ’γγγΌγγ―γηΉγ«γοΏ½?γγΈγγ―γη°γͺγγ³γ³γγΌγγ³γιγ§εε©η¨γγγε ΄εγ«ζη¨γ§γγ