If you have spent any time in React, you have seen the yellow warning in your console: “Each child in a list should have a unique ‘key’ prop.” It sounds like a polite suggestion, but React is actually warning you that it cannot tell your list items apart. Ignore it, and you will eventually ship a bug that is maddening to reproduce—state jumping to the wrong row, text inputs losing focus, or animations firing on the wrong element.
React’s rendering engine does not compare your UI pixel by pixel. It builds a lightweight tree of objects called the virtual DOM, compares the new tree to the previous one, and calculates the smallest set of changes needed for the real DOM. When you render a list, React sees an array of sibling elements. Without keys, it has no reliable way to know whether an item moved, was replaced, or was removed. It defaults to matching by position, which is fragile. Keys act as stable identities. They tell React, “This element is the same one as before, even if it is now in a different slot.” Get this wrong, and you trade deterministic updates for guesswork.
குறைந்தபட்சத் தீர்வு
இந்த எச்சரிக்கை பொதுவாக ஒரு map அழைப்பிற்குள் தோன்றும். இட்டரேட்டரிலிருந்து (iterator) திரும்பப் பெறப்படும் மேல்நிலை உறுப்பில் (top-level element) நீங்கள் ஒரு தனித்துவமான மதிப்பினை key பண்பிற்கு (attribute) ஒதுக்க வேண்டும்.
எச்சரிக்கையைத் தூண்டும் ஒவ்வொரு codebase-லும் நீங்கள் காணும் முறை இதோ:
const UserList = ({ users }) => {
return (
<ul>
{users.map((user) => (
<li>{user.name}</li>
))}
</ul>
);
};
React மூன்று <li>` டேக்குகளைப் பார்க்கிறது, ஆனால் எது எதுவென்று அதற்குத் தெரியவில்லை. இதற்கான திருத்தம் ஒரே ஒரு பண்பு மட்டுமே:
const UserList = ({ users }) => {
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
map callback-க்குள் இருக்கும் உறுப்பிற்கு நேரடியாக key ஒதுக்கப்பட வேண்டும். நீங்கள் <li>என்பதைத் தனிUserItem` காம்போனென்ட்டாக (component) மாற்றினாலும், key என்பது அழைப்பு இடத்திலேயே (call site) அந்த காம்போனென்ட்டிற்குச் சொந்தமானது:
{users.map((user) => (
<UserItem key={user.id} user={user} />
))}
UserItem-க்குள் இருக்கும் அதன் உட்புற <div>`-இல் key-ஐ வைப்பது எச்சரிக்கையைத் தடுத்துவிடாது மற்றும் reconciliation செயல்பாட்டையும் சரிசெய்யாது. இட்டரேட்டரால் திரும்பப் பெறப்படும் உறுப்பிலேயே React key-ஐத் தேடுகிறது.
ஏன் Index-ஐ Key-ஆகப் பயன்படுத்துவது ஆபத்தானது
map-இன் இரண்டாவது வாதத்தைப் (argument) பயன்படுத்தி எச்சரிக்கையைச் சத்தமில்லாமல் செய்வது கவர்ச்சிகரமானதாகத் தோன்றலாம்:
{users.map((user, index) => (
<li key={index}>{user.name}</li>
))}
இது console-இல் வரும் சத்தத்தைக் குறைக்கும், ஆனால் அடிப்படையான சிக்கலைத் தீர்க்காது. Array indices என்பவை அடையாளங்கள் (identities) அல்ல. அவை நிலைகள் (positions), நிலைகள் மாறக்கூடியவை.
மூன்று பயனர்களை இந்த வரிசையில் இருப்பதாகக் கற்பனை செய்து கொள்ளுங்கள்:
- Alice (index 0)
- Bob (index 1)
- Charlie (index 2)
நீங்கள் Alice-ஐ நீக்கினால், Bob index 0-விற்கும் Charlie index 1-விற்கும் நகர்கிறார். React புதிய மரத்தை பழைய மரத்துடன் ஒப்பிடுகிறது. இப்போது index 0-வில் Bob-இன் தரவு இருப்பதை அது பார்க்கிறது, எனவே முன்பு Alice-ஐக் காட்டிய அதே DOM நோடை (node) அது மாற்றியமைக்கிறது (mutate). அந்த நோடில் focus இருந்திருந்தால், கர்சர் முதல் வரிவிலேயே இருக்கும், ஆனால் உரை Bob-ஆக மாறிவிடும். அந்த வரிசையில் உள்ளூர் நிலை (local state) கொண்ட ஓர் <input> இருந்தால், அந்த நிலை index 0-விலேயே தங்கிவிடும். பயனர் Bob-இன் வரிசை என்று நினைத்துத் தட்டச்சு செய்வார், ஆனால் அந்த நிலை Alice-க்குச் சொந்தமானது. நீங்கள் உறுப்புகளை வரிசைப்படுத்தும்போது (sort), வடிகட்டும்போது (filter) அல்லது முன்னால் சேர்க்கும்போது (prepend) இதே குழப்பம் ஏற்படும். ஒரு index key-ஐப் பயன்படுத்த பாதுகாப்பான ஒரே இடம், உண்மையில் நிலையான (static) பட்டியலாகும்: அதாவது மறுவரிசைப்படுத்துதல், வடிகட்டுதல், சேர்த்தல் அல்லது நீக்குதல் எதுவும் இல்லாத பட்டியல். எப்போதும் மாறாத hardcoded நேவிகேஷன் லிங்க்குகள் இதற்குச் சரியான உதாரணம். மற்ற அனைத்திற்கும் ஒரு உண்மையான அடையாளங்காட்டி (identifier) தேவை.
ஒரு நிலையான Key-ஐ எங்கே கண்டறிவது
சிறந்த key என்பது உங்கள் தரவு மாதிரியில் (data model) ஏற்கனவே இருக்கும் ஒரு தனித்துவமான அடையாளங்காட்டியாகும். id போன்ற தரவுத்தள முதன்மைத் திறவுகோல்கள் (Database primary keys) சிறந்தவை, ஏனெனில் அவை தனித்துவமானவை என்பது உறுதி செய்யப்பட்டுள்ளது மற்றும் அவை ரெண்டர்களுக்கும் அப்பாலும் நிலைத்திருக்கும். உங்கள் backend uuid, slug அல்லது வேறு ஏதேனும் இயற்கையாகத் தனித்துவமான புலத்தை (field) வழங்கினால், அதற்குப் பதிலாக அதைப் பயன்படுத்தவும்.
உங்கள் API பதிலில் எந்தத் தனித்துவமான புலமும் இல்லாதபோது, உங்களிடம் இரண்டு நடைமுறை வழிகள் உள்ளன. முதலாவதாக, உங்கள் backend குழுவிடம் பேசி ஒரு id-ஐச் சேர்க்கச் சொல்லுங்கள். முதன்மைத் திறவுகோல் இல்லாமல் தொடர்புடைய தரவை (relational data) அனுப்புவது ஒரு தவறான நடைமுறை (smell), மேலும் மூலத்திலேயே (source) அதைச் சரிசெய்வது உங்கள் முழு stack-லும் உள்ள தெளிவற்ற நிலையை நீக்கும். இரண்டாவதாக, நீங்கள் முழுமையாக கிளையண்ட் பக்கத்தில் (client side) உறுப்புகளை உருவாக்கினால்—உதாரணமாக, பயனர்கள் சர்வர் எதையும் தொடங்கும் முன்பே பணிகளை உருவாக்கும் ஒரு todo list—உருவாக்கும் நேரத்திலேயே ஒரு ID-ஐ உருவாக்கிவிடுங்கள். uuid அல்லது nanoid போன்ற லைப்ரரிகள் இதற்காகவே உருவாக்கப்பட்டுள்ளன. பயனர் படிவத்தைச் சமர்ப்பிக்கும்போது ID-ஐ உருவாக்கி, அதை ஆப்ஜெக்ட்டில் சேமித்து, எப்போதும் key-க்காகப் பயன்படுத்துங்கள்.
ரெண்டர் பாதையில் (render path) ஒருபோதும் key-ஐ உருவாக்காதீர்கள். ஒரு காம்போனென்ட் ரெண்டர் ஆகும் போது Math.random() அல்லது Date.now()-ஐ அழைப்பது ஒவ்வொரு முறையும் ஒரு புதிய மதிப்பினை உருவாக்கும். React ஒரு புதிய key-ஐப் பார்க்கிறது, அது ஒரு புதிய உறுப்பு என்று கருதுகிறது, பழைய DOM நோடை அழித்துவிட்டுப் புதிய ஒன்றைப் படைக்கிறது. அந்த உறுப்பிற்குள் இருக்கும் எந்த நிலையும் (state) ரீசெட் ஆகிவிடும். Focus இழக்கப்படும். React தேவையற்ற DOM வேலைகளைச் செய்வதால் செயல்திறன் (performance) குறையும். சீரற்ற முறையில் (randomly) உருவாக்கப்படும் ஒரு key, ஒரு key இல்லையிருப்பதை விட மோசமானது.
Fragments, Components, மற்றும் Scope
அவ்வளவு எளிதில் தெரியாத ஒரு சிக்கல் React Fragments-இல் உள்ளது. நீங்கள் தரவுகளை (data) map செய்யும்போது, ஒரு <div> போன்ற wrapper இல்லாமல் பல உடன் பிறந்த உறுப்புகளை (sibling elements) திருப்பி அனுப்ப வேண்டியிருந்தால், நீங்கள் இந்தச் சுருக்கமான வடிவத்தைப் பயன்படுத்தலாம்:
{items.map((item) => (
<>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</>
))}
இந்தச் சுருக்க வடிவான <>...</> props-களை ஆதரிக்காது, அதாவது உங்களால் ஒரு key-ஐ இதில் இணைக்க முடியாது. இத்தகைய சூழலில், முழுமையான தெளிவான வடிவத்திற்கு (explicit syntax) மாறவும்:
{items.map((item) => (
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.definition}</dd>
</React.Fragment>
))}
React அந்த ஜோடியை render-களுக்கு இடையே ஒரு தனி அலகாகக் கண்காணிக்க, Fragment-இல் அந்த key அவசியம் தேவைப்படுகிறது.
மற்றொரு நுணுக்கமான விஷயம்: keys என்பது வழக்கமான props போன்றது அல்ல. நீங்கள் <ListItem key={item.id} /> என்று எழுதினால், ListItem component-ஆல் props.key-ஐப் படிக்க முடியாது. React இதைத் தனது கணக்குத் தரவுகளைப் பராமரிக்க (bookkeeping) தனக்குள்ளேயே பயன்படுத்திக்கொள்கிறது. உங்கள் component-க்கு அதன் சொந்த தர்க்கத்திற்கு (logic) அந்த அடையாளங்காட்டி (identifier) உண்மையிலேயே தேவைப்பட்டால், அதை itemId போன்ற வேறு ஒரு பெயரில் தனியாக அனுப்பவும்.
உங்களைப் பாதுகாப்பாக வைத்திருக்க சில நடைமுறை விதிகள்
- Database IDs-களையே முன்னுரிமைப்படுத்துங்கள். அவை தனித்துவமானவை, எண்கள் அல்லது சரங்களை (string) அடிப்படையாகக் கொண்டவை மற்றும் நிலையானவை.
- Client-only தரவுகளுக்கு
uuidஅல்லதுnanoid-ஐப் பயன்படுத்தவும். தரவுப் பதிவு (record) உருவாக்கப்படும்போது ஒருமுறை மட்டும் ID-ஐ உருவாக்கவும், component render-க்குள் அல்ல. - பட்டியல் (list) மாறக்கூடியதாக இருந்தால், ஒருபோதும் array index-லிருந்து key-ஐப் பெற வேண்டாம். வரிசைப்படுத்துதல் (sorting), வடிகட்டுதல் (filtering) மற்றும் நீக்குதல் (deleting) ஆகியவை காட்சி மற்றும் நிலை (state) சார்ந்த பிழைகளை (bugs) உருவாக்கும்.
Math.random(),Date.now()அல்லது render-களுக்கு இடையே மாறும் எந்த மதிப்பையும் ஒருபோதும் பயன்படுத்த வேண்டாம். இது தேவையற்ற unmounting மற்றும் remounting-க்கு வழிவகுக்கும்.- Fragments ஒரு
map-க்குள் இருந்து, அதற்கு ஒரு key தேவைப்பட்டால், அதன் முழுமையான வடிவத்தைப் பயன்படுத்த வேண்டும் என்பதை நினைவில் கொள்ளுங்கள். - key-ஐ
map-ஆல் திருப்பி அனுப்பப்படும் உறுப்பின் (element) மீது வைக்கவும், ஒரு child component-க்குள் வைக்க வேண்டாம்.
முக்கியமான கருத்து
key prop என்பது வெறும் அலங்காரமான lint விதி அல்ல. render-களுக்கு இடையே React தனது அடையாளத்தைப் பராமரிக்கும் வழியாக இதுவே. இதை ஒரு database அட்டவணையில் உள்ள primary key போலக் கருதலாம். அந்த அடையாளம் நிலையானதாக இருக்கும்போது, React உறுப்புகளைத் துல்லியமாக நகர்த்தவும், புதுப்பிக்கவும் மற்றும் நீக்கவும் முடியும். அது இல்லையென்றால் அல்லது நிலையற்றதாக இருந்தால், சிதைந்த UI state மற்றும் மெதுவான reconciliation போன்ற பாதிப்புகளைச் சந்திக்க நேரிடும். தரவு நிலையில் (data layer) இதை ஒருமுறை சரிசெய்துவிட்டால், உங்கள் பட்டியல்கள் எவ்வளவு வளர்ந்தாலும் அல்லது மாறினாலும் அவை கணிக்கக்கூடிய வகையில் செயல்படும்.
