cloneElement
மற்றொரு element-ஐ starting point ஆகப் பயன்படுத்தி புதிய React element உருவாக்க cloneElement உதவுகிறது.
const clonedElement = cloneElement(element, props, ...children)குறிப்பு
cloneElement(element, props, ...children)
element அடிப்படையில், ஆனால் வேறுபட்ட props மற்றும் children உடன் React element உருவாக்க cloneElement-ஐ call செய்யுங்கள்:
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>மேலும் உதாரணங்களை கீழே பார்க்கவும்.
Parameters
-
element:elementargument valid React element ஆக இருக்க வேண்டும். உதாரணமாக, அது<Something />போன்ற JSX node,createElementcall செய்த result, அல்லது மற்றொருcloneElementcall-ன் result ஆக இருக்கலாம். -
props:propsargument object அல்லதுnullஆக இருக்க வேண்டும். நீங்கள்nullpass செய்தால், cloned element originalelement.propsஅனைத்தையும் retain செய்யும். இல்லையெனில்,propsobject-இல் உள்ள ஒவ்வொரு prop-க்கும், return செய்யப்படும் elementelement.props-இல் உள்ள value-ஐ விடprops-இல் உள்ள value-ஐ “prefer” செய்யும். மீதமுள்ள props originalelement.props-இலிருந்து நிரப்பப்படும்.props.keyஅல்லதுprops.refpass செய்தால், அவை original values-ஐ replace செய்யும். -
optional
...children: பூஜ்யம் அல்லது அதற்கு மேற்பட்ட child nodes. அவை React elements, strings, numbers, portals, empty nodes (null,undefined,true, மற்றும்false), மற்றும் React nodes-ன் arrays உட்பட எந்த React nodes ஆகவும் இருக்கலாம்....childrenarguments எதையும் pass செய்யவில்லை என்றால், originalelement.props.childrenpreserve செய்யப்படும்.
Returns
cloneElement சில properties கொண்ட React element object-ஐ return செய்கிறது:
type:element.typeபோலவே இருக்கும்.props: நீங்கள் pass செய்த overridingpropsஉடன்element.props-ஐ shallow-ஆக merge செய்த result.ref:props.refமூலம் override செய்யப்படாதவரை originalelement.ref.key:props.keyமூலம் override செய்யப்படாதவரை originalelement.key.
பொதுவாக, உங்கள் component-இலிருந்து element-ஐ return செய்வீர்கள் அல்லது அதை மற்றொரு element-ன் child ஆக்குவீர்கள். Element-ன் properties-ஐ read செய்யலாம் என்றாலும், element உருவாக்கப்பட்ட பிறகு அதை opaque ஆகக் கருதி render செய்வதே சிறந்தது.
Caveats
-
Element-ஐ clone செய்வது original element-ஐ modify செய்யாது.
-
எல்லா children-உம் statically known ஆக இருந்தால் மட்டுமே
cloneElement(element, null, child1, child2, child3)போலcloneElement-க்கு children-ஐ பல arguments ஆக pass செய்யுங்கள். உங்கள் children dynamic என்றால், முழு array-ஐ மூன்றாவது argument ஆக pass செய்யுங்கள்:cloneElement(element, null, listItems). இதனால் dynamic lists-க்கு missingkeys பற்றி React warning வழங்கும். Static lists-க்கு இது அவசியமில்லை; ஏனெனில் அவை reorder ஆகாது. -
cloneElementdata flow trace செய்வதை கடினமாக்குகிறது; எனவே அதற்கு பதிலாக மாற்று வழிகளை முயற்சிக்கவும்.
பயன்பாடு
Element-ன் props-ஐ override செய்தல்
ஒரு React element-ன் props-ஐ override செய்ய, override செய்ய வேண்டிய props உடன் அதை cloneElement-க்கு pass செய்யுங்கள்:
import { cloneElement } from 'react';
// ...
const clonedElement = cloneElement(
<Row title="Cabbage" />,
{ isHighlighted: true }
);இங்கு resulting cloned element <Row title="Cabbage" isHighlighted={true} /> ஆக இருக்கும்.
இது எப்போது பயனுள்ளதாக இருக்கும் என்பதை ஒரு உதாரணத்தின் மூலம் பார்ப்போம்.
தேர்வு செய்யக்கூடிய rows பட்டியலாக தனது children-ஐ render செய்யும் List component-ஐ கற்பனை செய்யுங்கள்; எந்த row தேர்வு செய்யப்படுகிறது என்பதை மாற்றும் “Next” button உள்ளது. List component தேர்வு செய்யப்பட்ட Row-ஐ வேறுபட்டு render செய்ய வேண்டும்; ஆகவே அது பெற்ற ஒவ்வொரு <Row> child-ஐயும் clone செய்து, கூடுதல் isHighlighted: true அல்லது isHighlighted: false prop சேர்க்கிறது:
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 பெற்ற original JSX இவ்வாறு இருப்பதாக வைத்துக் கொள்வோம்:
<List>
<Row title="Cabbage" />
<Row title="Garlic" />
<Row title="Apple" />
</List>தன் children-ஐ clone செய்வதன் மூலம், List உள்ளே உள்ள ஒவ்வொரு Row-க்கும் extra information pass செய்ய முடியும். Result இவ்வாறு இருக்கும்:
<List>
<Row
title="Cabbage"
isHighlighted={true}
/>
<Row
title="Garlic"
isHighlighted={false}
/>
<Row
title="Apple"
isHighlighted={false}
/>
</List>“Next” press செய்தால் List-ன் state update ஆகி, வேறு row highlight ஆகிறது என்பதை கவனியுங்கள்:
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 /> elements-ஐ clone செய்து, அவற்றுக்கு extra prop சேர்த்தது.
மாற்று வழிகள்
Render prop மூலம் data pass செய்தல்
cloneElement பயன்படுத்துவதற்கு பதிலாக, renderItem போன்ற render prop ஏற்கலாம். இங்கு List renderItem-ஐ prop ஆகப் பெறுகிறது. ஒவ்வொரு item-க்கும் List renderItem call செய்து, isHighlighted-ஐ argument ஆக pass செய்கிறது:
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 prop-ஐ “render prop” என்று அழைப்பது, ஏதாவது ஒன்றை எப்படி render செய்ய வேண்டும் என்பதை குறிப்பிடும் prop என்பதால். உதாரணமாக, கொடுக்கப்பட்ட isHighlighted value உடன் <Row> render செய்யும் renderItem implementation-ஐ pass செய்யலாம்:
<List
items={products}
renderItem={(product, isHighlighted) =>
<Row
key={product.id}
title={product.title}
isHighlighted={isHighlighted}
/>
}
/>இறுதி result cloneElement பயன்படுத்தியதுடன் ஒரே மாதிரியாக இருக்கும்:
<List>
<Row
title="Cabbage"
isHighlighted={true}
/>
<Row
title="Garlic"
isHighlighted={false}
/>
<Row
title="Apple"
isHighlighted={false}
/>
</List>ஆனால் isHighlighted value எங்கிருந்து வருகிறது என்பதை தெளிவாக trace செய்ய முடியும்.
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> ); }
இந்த pattern cloneElement-ஐ விட விரும்பத்தக்கது; ஏனெனில் இது இன்னும் explicit.
Context மூலம் data pass செய்தல்
cloneElement-க்கு மற்றொரு மாற்று வழி context மூலம் data pass செய்வது.
உதாரணமாக, HighlightContext define செய்ய createContext-ஐ call செய்யலாம்:
export const HighlightContext = createContext(false);உங்கள் List component அது render செய்யும் ஒவ்வொரு item-ஐயும் HighlightContext provider-க்குள் wrap செய்யலாம்:
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 key={item.id} value={isHighlighted}>
{renderItem(item)}
</HighlightContext>
);
})}இந்த அணுகுமுறையில், Row isHighlighted prop பெறவே தேவையில்லை. அதற்கு பதிலாக அது context-ஐ read செய்கிறது:
export default function Row({ title }) {
const isHighlighted = useContext(HighlightContext);
// ...இதனால் calling component <Row>-க்கு isHighlighted pass செய்வதைப் பற்றி அறியவோ கவலைப்படவோ வேண்டியதில்லை:
<List
items={products}
renderItem={product =>
<Row title={product.title} />
}
/>அதற்கு பதிலாக, List மற்றும் Row highlighting logic-ஐ context மூலம் coordinate செய்கின்றன.
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 key={item.id} value={isHighlighted} > {renderItem(item)} </HighlightContext> ); })} <hr /> <button onClick={() => { setSelectedIndex(i => (i + 1) % items.length ); }}> Next </button> </div> ); }
Context மூலம் data pass செய்வது பற்றி மேலும் அறிக.
Logic-ஐ custom Hook-க்குள் extract செய்தல்
நீங்கள் முயற்சிக்கக்கூடிய மற்றொரு அணுகுமுறை: “non-visual” logic-ஐ உங்கள் சொந்த Hook-க்குள் extract செய்து, உங்கள் Hook return செய்யும் information-ஐ வைத்து என்ன render செய்ய வேண்டும் என்பதை தீர்மானிப்பது. உதாரணமாக, useList custom Hook-ஐ இவ்வாறு எழுதலாம்:
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>
);
}Data flow explicit ஆக உள்ளது; ஆனால் state எந்த component-இலிருந்தும் பயன்படுத்தக்கூடிய useList custom Hook-க்குள் உள்ளது:
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> ); }
இந்த logic-ஐ வெவ்வேறு components இடையே reuse செய்ய விரும்பினால், இந்த அணுகுமுறை குறிப்பாக பயனுள்ளது.