Context மூலம் data-வை ஆழமாக pass செய்தல்
பொதுவாக, parent component-இலிருந்து child component-க்கு props மூலம் information pass செய்வீர்கள். ஆனால் பல intermediate components வழியாக அவற்றை pass செய்ய வேண்டியிருந்தால், அல்லது உங்கள் app-இல் பல components-க்கு ஒரே information தேவைப்பட்டால், props pass செய்வது verbose மற்றும் inconvenient ஆகலாம். Context parent component-க்கு, அதன் கீழுள்ள tree-இல் எவ்வளவு ஆழத்தில் இருந்தாலும் எந்த component-க்கும், props மூலம் explicit ஆக pass செய்யாமல் சில information கிடைக்கச் செய்ய அனுமதிக்கிறது.
நீங்கள் கற்றுக்கொள்ள போவது
- ”Prop drilling” என்றால் என்ன
- Repetitive prop passing-ஐ context மூலம் மாற்றுவது எப்படி
- Context-க்கான பொதுவான use cases
- Context-க்கான பொதுவான alternatives
Props pass செய்வதில் உள்ள பிரச்சினை
Props pass செய்வது, உங்கள் UI tree வழியாக data-வை அதை பயன்படுத்தும் components-க்கு explicit ஆக pipe செய்ய சிறந்த வழி.
ஆனால் tree-இல் ஆழமாக ஒரு prop pass செய்ய வேண்டியிருந்தால், அல்லது பல components-க்கு ஒரே prop தேவைப்பட்டால், props pass செய்வது verbose மற்றும் inconvenient ஆகலாம். Data தேவைப்படும் components-இலிருந்து nearest common ancestor மிகவும் தூரமாக இருக்கலாம்; அத்தனை உயரத்தில் state-ஐ lift up செய்வது “prop drilling” என்று அழைக்கப்படும் நிலைக்கு வழிவகுக்கலாம்.
State-ஐ மேலே lift செய்தல்


Prop drilling


Props pass செய்யாமல் data தேவைப்படும் tree components-க்கு அதை “teleport” செய்ய வழி இருந்தால் நன்றாக இருக்காதா? React-ன் context feature மூலம் அது சாத்தியம்!
Context: props pass செய்வதற்கான alternative
Context ஒரு parent component-க்கு அதன் கீழுள்ள முழு tree-க்கும் data provide செய்ய அனுமதிக்கிறது. Context-க்கு பல uses உள்ளன. இதோ ஒரு example. Size-க்காக level accept செய்யும் இந்த Heading component-ஐ கவனியுங்கள்:
import Heading from './Heading.js'; import Section from './Section.js'; export default function Page() { return ( <Section> <Heading level={1}>தலைப்பு</Heading> <Heading level={2}>Heading</Heading> <Heading level={3}>துணை-heading</Heading> <Heading level={4}>துணை-துணை-heading</Heading> <Heading level={5}>துணை-துணை-துணை-heading</Heading> <Heading level={6}>துணை-துணை-துணை-துணை-heading</Heading> </Section> ); }
ஒரே Section-க்குள் உள்ள பல headings எப்போதும் ஒரே size-இல் இருக்க வேண்டும் என்று வைத்துக் கொள்ளுங்கள்:
import Heading from './Heading.js'; import Section from './Section.js'; export default function Page() { return ( <Section> <Heading level={1}>தலைப்பு</Heading> <Section> <Heading level={2}>Heading</Heading> <Heading level={2}>Heading</Heading> <Heading level={2}>Heading</Heading> <Section> <Heading level={3}>துணை-heading</Heading> <Heading level={3}>துணை-heading</Heading> <Heading level={3}>துணை-heading</Heading> <Section> <Heading level={4}>துணை-துணை-heading</Heading> <Heading level={4}>துணை-துணை-heading</Heading> <Heading level={4}>துணை-துணை-heading</Heading> </Section> </Section> </Section> </Section> ); }
தற்போது, ஒவ்வொரு <Heading>-க்கும் level prop-ஐ தனித்தனியாக pass செய்கிறீர்கள்:
<Section>
<Heading level={3}>பற்றி</Heading>
<Heading level={3}>புகைப்படங்கள்</Heading>
<Heading level={3}>வீடியோக்கள்</Heading>
</Section>அதற்கு பதிலாக level prop-ஐ <Section> component-க்கு pass செய்து <Heading>-இலிருந்து remove செய்ய முடிந்தால் நன்றாக இருக்கும். இதனால் ஒரே section-இல் உள்ள அனைத்து headings-க்கும் ஒரே size என்பதை enforce செய்யலாம்:
<Section level={3}>
<Heading>பற்றி</Heading>
<Heading>புகைப்படங்கள்</Heading>
<Heading>வீடியோக்கள்</Heading>
</Section>ஆனால் <Heading> component தனது closest <Section>-ன் level-ஐ எப்படி அறியும்? அதற்கு, child ஒன்று tree-இல் மேலே எங்கோ உள்ள data-வை “கேட்க” ஒரு வழி தேவைப்படும்.
Props மட்டும் கொண்டு அதைச் செய்ய முடியாது. இங்கேதான் context பயன்படுகிறது. இதை மூன்று steps-இல் செய்வீர்கள்:
- Context ஒன்றை உருவாக்குங்கள். (Heading level-க்காக இருப்பதால் அதை
LevelContextஎன்று அழைக்கலாம்.) - Data தேவைப்படும் component-இல் அந்த context-ஐ பயன்படுத்துங்கள். (
HeadingLevelContext-ஐ பயன்படுத்தும்.) - Data specify செய்யும் component-இலிருந்து அந்த context-ஐ provide செய்யுங்கள். (
SectionLevelContextprovide செய்யும்.)
Context ஒரு parent-க்கு—தூரத்தில் இருந்தாலும்!—அதன் உள்ளேயுள்ள முழு tree-க்கும் data provide செய்ய அனுமதிக்கிறது.
அருகிலுள்ள children-இல் context பயன்படுத்துதல்


தொலைவில் உள்ள children-இல் context பயன்படுத்துதல்


படி 1: Context உருவாக்குங்கள்
முதலில், context உருவாக்க வேண்டும். உங்கள் components அதை பயன்படுத்த முடியும் வகையில் அதை ஒரு file-இலிருந்து export செய்ய வேண்டும்:
import { createContext } from 'react'; export const LevelContext = createContext(1);
createContext-க்கு ஒரே argument default value. இங்கே, 1 மிகப்பெரிய heading level-ஐ குறிக்கிறது; ஆனால் எந்த kind value-யையும் (object கூட) pass செய்யலாம். Default value-ன் முக்கியத்துவத்தை அடுத்த step-இல் பார்ப்பீர்கள்.
படி 2: Context பயன்படுத்துங்கள்
React-இலிருந்து useContext Hook-ஐயும், உங்கள் context-ஐயும் import செய்யுங்கள்:
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';தற்போது, Heading component props-இலிருந்து level read செய்கிறது:
export default function Heading({ level, children }) {
// ...
}அதற்கு பதிலாக, level prop-ஐ remove செய்து, இப்போது import செய்த LevelContext context-இலிருந்து value read செய்யுங்கள்:
export default function Heading({ children }) {
const level = useContext(LevelContext);
// ...
}useContext ஒரு Hook. useState மற்றும் useReducer போலவே, Hook-ஐ React component-க்குள் உடனடியாக மட்டுமே call செய்யலாம் (loops அல்லது conditions-க்குள் அல்ல). Heading component LevelContext-ஐ read செய்ய விரும்புகிறது என்று useContext React-க்கு சொல்கிறது.
இப்போது Heading component-க்கு level prop இல்லாததால், இனி JSX-இல் இதுபோல் level prop-ஐ Heading-க்கு pass செய்ய தேவையில்லை:
<Section>
<Heading level={4}>துணை-துணை-heading</Heading>
<Heading level={4}>துணை-துணை-heading</Heading>
<Heading level={4}>துணை-துணை-heading</Heading>
</Section>அதற்கு பதிலாக அதை receive செய்வது Section ஆக இருக்க JSX-ஐ update செய்யுங்கள்:
<Section level={4}>
<Heading>துணை-துணை-heading</Heading>
<Heading>துணை-துணை-heading</Heading>
<Heading>துணை-துணை-heading</Heading>
</Section>நீங்கள் வேலை செய்யச் செய்ய முயன்ற markup இதுதான் என்பதை நினைவில் கொள்ளுங்கள்:
import Heading from './Heading.js'; import Section from './Section.js'; export default function Page() { return ( <Section level={1}> <Heading>தலைப்பு</Heading> <Section level={2}> <Heading>Heading</Heading> <Heading>Heading</Heading> <Heading>Heading</Heading> <Section level={3}> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Section level={4}> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> </Section> </Section> </Section> </Section> ); }
இந்த example இன்னும் முழுமையாக வேலை செய்யவில்லை என்பதை கவனியுங்கள்! அனைத்து headings-க்கும் ஒரே size உள்ளது, ஏனெனில் நீங்கள் context-ஐ பயன்படுத்துகிறீர்கள், ஆனால் அதை இன்னும் provide செய்யவில்லை. அதை எங்கிருந்து பெற வேண்டும் என்று React-க்கு தெரியாது!
Context provide செய்யவில்லை என்றால், முந்தைய step-இல் நீங்கள் specify செய்த default value-ஐ React பயன்படுத்தும். இந்த example-இல், createContext-க்கு argument ஆக 1 specify செய்ததால், useContext(LevelContext) 1 return செய்கிறது; அதனால் அந்த headings அனைத்தும் <h1> ஆகின்றன. ஒவ்வொரு Section-மும் தனது context-ஐ provide செய்யச் செய்வதன் மூலம் இந்த பிரச்சினையை fix செய்வோம்.
படி 3: Context provide செய்யுங்கள்
Section component தற்போது தனது children-ஐ render செய்கிறது:
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}அவற்றுக்கு LevelContext provide செய்ய, அவற்றை context provider-இல் wrap செய்யுங்கள்:
import { LevelContext } from './LevelContext.js';
export default function Section({ level, children }) {
return (
<section className="section">
<LevelContext value={level}>
{children}
</LevelContext>
</section>
);
}இது React-க்கு சொல்வது: “இந்த <Section>-க்குள் உள்ள எந்த component LevelContext கேட்டாலும், இதன் level-ஐ கொடு.” Component, UI tree-இல் மேலே உள்ள nearest <LevelContext> value-ஐ பயன்படுத்தும்.
import Heading from './Heading.js'; import Section from './Section.js'; export default function Page() { return ( <Section level={1}> <Heading>தலைப்பு</Heading> <Section level={2}> <Heading>Heading</Heading> <Heading>Heading</Heading> <Heading>Heading</Heading> <Section level={3}> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Section level={4}> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> </Section> </Section> </Section> </Section> ); }
Original code-இன் அதே result தான், ஆனால் ஒவ்வொரு Heading component-க்கும் level prop pass செய்ய வேண்டியதில்லை! அதற்கு பதிலாக, அது மேலே உள்ள closest Section-இடம் கேட்டு தனது heading level-ஐ “கண்டுபிடிக்கிறது”:
<Section>-க்குlevelprop pass செய்கிறீர்கள்.Sectionதனது children-ஐ<LevelContext value={level}>-க்குள் wrap செய்கிறது.HeadinguseContext(LevelContext)மூலம் மேலுள்ள closestLevelContextvalue-ஐ கேட்கிறது.
அதே component-இல் context பயன்படுத்தியும் provide செய்தலும்
தற்போது, ஒவ்வொரு section-ன் level-ஐ இன்னும் manually specify செய்ய வேண்டும்:
export default function Page() {
return (
<Section level={1}>
...
<Section level={2}>
...
<Section level={3}>
...Context மேலுள்ள component-இலிருந்து information read செய்ய அனுமதிப்பதால், ஒவ்வொரு Section-மும் மேலுள்ள Section-இலிருந்து level-ஐ read செய்து, level + 1-ஐ தானாக கீழே pass செய்யலாம். இதை இப்படிச் செய்யலாம்:
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';
export default function Section({ children }) {
const level = useContext(LevelContext);
return (
<section className="section">
<LevelContext value={level + 1}>
{children}
</LevelContext>
</section>
);
}இந்த change மூலம், <Section>-க்கும் <Heading>-க்கும் level prop pass செய்யவே தேவையில்லை:
import Heading from './Heading.js'; import Section from './Section.js'; export default function Page() { return ( <Section> <Heading>தலைப்பு</Heading> <Section> <Heading>Heading</Heading> <Heading>Heading</Heading> <Heading>Heading</Heading> <Section> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Heading>துணை-heading</Heading> <Section> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> <Heading>துணை-துணை-heading</Heading> </Section> </Section> </Section> </Section> ); }
இப்போது Heading மற்றும் Section இரண்டும் அவை எவ்வளவு “deep” என கண்டுபிடிக்க LevelContext read செய்கின்றன. மேலும் Section, அதன் உள்ளே உள்ள எதுவும் “deeper” level-இல் உள்ளது என்று specify செய்ய தனது children-ஐ LevelContext-க்குள் wrap செய்கிறது.
Context intermediate components வழியாக pass ஆகிறது
Context provide செய்யும் component மற்றும் அதை பயன்படுத்தும் component இடையே உங்களுக்கு விருப்பமான அளவு components insert செய்யலாம். இதில் <div> போன்ற built-in components மற்றும் நீங்கள் build செய்யும் components இரண்டும் அடங்கும்.
இந்த example-இல், அதே Post component (dashed border உடன்) இரண்டு வெவ்வேறு nesting levels-இல் render செய்யப்படுகிறது. அதன் உள்ளே உள்ள <Heading> தனது level-ஐ closest <Section>-இலிருந்து தானாக பெறுகிறது என்பதை கவனியுங்கள்:
import Heading from './Heading.js'; import Section from './Section.js'; export default function ProfilePage() { return ( <Section> <Heading>என் Profile</Heading> <Post title="வணக்கம் traveller!" body="என் adventures பற்றி வாசிக்கவும்." /> <AllPosts /> </Section> ); } function AllPosts() { return ( <Section> <Heading>Posts</Heading> <RecentPosts /> </Section> ); } function RecentPosts() { return ( <Section> <Heading>சமீபத்திய Posts</Heading> <Post title="Lisbon-ன் சுவைகள்" body="...அந்த pastéis de nata!" /> <Post title="Tango தாளத்தில் Buenos Aires" body="எனக்கு மிகவும் பிடித்தது!" /> </Section> ); } function Post({ title, body }) { return ( <Section isFancy={true}> <Heading> {title} </Heading> <p><i>{body}</i></p> </Section> ); }
இது வேலை செய்ய நீங்கள் எந்த special செயலும் செய்யவில்லை. ஒரு Section அதன் உள்ளேயுள்ள tree-க்கான context-ஐ specify செய்கிறது; எனவே <Heading>-ஐ எங்கு insert செய்தாலும், அது சரியான size பெறும். மேலுள்ள sandbox-இல் முயற்சிக்கவும்!
Context, “தங்கள் surroundings-க்கு adapt ஆகும்” components எழுத அனுமதிக்கிறது; அவை எங்கு (அல்லது வேறு வார்த்தைகளில், எந்த context-இல்) render செய்யப்படுகின்றன என்பதைப் பொறுத்து தங்களை வேறுபடக் display செய்யும்.
Context வேலை செய்வது CSS property inheritance-ஐ நினைவூட்டலாம். CSS-இல், <div>-க்கு color: blue specify செய்யலாம்; அதன் உள்ளே எவ்வளவு ஆழமாக இருந்தாலும் எந்த DOM node-மும், நடுவே உள்ள வேறு DOM node color: green மூலம் override செய்யாவிட்டால், அந்த color-ஐ inherit செய்யும். அதேபோல் React-இல், மேலிருந்து வரும் context-ஐ override செய்ய ஒரே வழி, வேறு value கொண்ட context provider-இல் children-ஐ wrap செய்வது.
CSS-இல், color மற்றும் background-color போன்ற வெவ்வேறு properties ஒன்றை ஒன்று override செய்யாது. அனைத்து <div>-களின் color-ஐ red ஆக set செய்தாலும் background-color பாதிக்கப்படாது. அதேபோல், வெவ்வேறு React contexts ஒன்றை ஒன்று override செய்யாது. createContext() மூலம் நீங்கள் உருவாக்கும் ஒவ்வொரு context-மும் மற்றவற்றிலிருந்து முற்றிலும் separate; அந்த particular context-ஐ use செய்து provide செய்யும் components-ஐ மட்டும் இணைக்கிறது. ஒரு component பிரச்சினையின்றி பல வெவ்வேறு contexts-ஐ use அல்லது provide செய்யலாம்.
Context பயன்படுத்துவதற்கு முன்
Context பயன்படுத்துவது மிகவும் tempting! ஆனால் இதன் பொருள் அதை overuse செய்வதும் சாத்தியம். சில props-ஐ several levels deep pass செய்ய வேண்டும் என்பதற்காகவே அந்த information-ஐ context-இல் வைக்க வேண்டும் என்று அர்த்தமில்லை.
Context பயன்படுத்துவதற்கு முன் நீங்கள் பரிசீலிக்க வேண்டிய சில alternatives:
- Props pass செய்வதிலிருந்து தொடங்குங்கள். உங்கள் components trivial அல்லாவிட்டால், dozen components வழியாக dozen props pass செய்வது unusual அல்ல. இது slog போல உணரப்படலாம்; ஆனால் எந்த components எந்த data-வை use செய்கின்றன என்பதை மிகவும் தெளிவாக்குகிறது! உங்கள் code maintain செய்யும் நபர் props மூலம் data flow explicit ஆக்கியதற்காக மகிழ்வார்.
- Components extract செய்து, அவற்றுக்கு JSX-ஐ
childrenஆக pass செய்யுங்கள். அந்த data-வை use செய்யாத intermediate components பல layers வழியாக data pass செய்தால் (அவை அதை மேலும் கீழே pass செய்கின்றன மட்டுமே), வழியில் சில components extract செய்ய மறந்திருக்கலாம் என்பதைக் குறிக்கும். உதாரணமாக,postsபோன்ற data props-ஐ நேரடியாக use செய்யாத visual components-க்கு pass செய்யலாம்:<Layout posts={posts} />. அதற்கு பதிலாக,Layoutchildrenprop எடுக்கச் செய்து,<Layout><Posts posts={posts} /></Layout>render செய்யுங்கள். இதனால் data specify செய்யும் component மற்றும் அதை தேவைப்படும் component இடையிலான layers எண்ணிக்கை குறையும்.
இந்த approaches எதுவும் உங்களுக்கு நன்றாக வேலை செய்யவில்லை என்றால், context-ஐ பரிசீலிக்கவும்.
Context-க்கான use cases
- Theming: உங்கள் app user-க்கு அதன் appearance மாற்ற அனுமதித்தால் (உதா. dark mode), உங்கள் app-ன் top-இல் context provider வைத்து, visual look adjust செய்ய வேண்டிய components-இல் அந்த context-ஐ பயன்படுத்தலாம்.
- Current account: தற்போது logged in user யார் என்பதை பல components அறிய வேண்டியிருக்கலாம். அதை context-இல் வைப்பது tree-இல் எங்கு வேண்டுமானாலும் read செய்வதை வசதியாக்குகிறது. சில apps ஒரே நேரத்தில் பல accounts operate செய்யவும் அனுமதிக்கின்றன (உதா. வேறு user ஆக comment விட). அத்தகைய சூழல்களில், UI-ன் ஒரு பகுதியை வேறு current account value கொண்ட nested provider-இல் wrap செய்வது வசதியாக இருக்கலாம்.
- Routing: பெரும்பாலான routing solutions current route வைத்திருக்க context-ஐ internally பயன்படுத்துகின்றன. இதனால் ஒவ்வொரு link-க்கும் அது active ஆக உள்ளதா இல்லையா “தெரியும்”. நீங்கள் சொந்த router build செய்தால், இதையும் செய்ய விரும்பலாம்.
- State manage செய்தல்: உங்கள் app வளரும்போது, app top-க்கு அருகில் நிறைய state இருக்கலாம். கீழே தொலைவில் உள்ள பல components அதை change செய்ய விரும்பலாம். Complex state manage செய்து அதை தொலைவில் உள்ள components-க்கு அதிக சிரமமின்றி pass செய்ய context உடன் reducer பயன்படுத்துவது பொதுவானது.
Context static values-க்கு மட்டும் கட்டுப்பட்டது அல்ல. அடுத்த render-இல் வேறு value pass செய்தால், அதை read செய்யும் கீழுள்ள அனைத்து components-யையும் React update செய்யும்! அதனால் context பெரும்பாலும் state உடன் சேர்த்து பயன்படுத்தப்படுகிறது.
பொதுவாக, tree-ன் வெவ்வேறு பகுதிகளில் உள்ள தொலைவிலான components-க்கு ஏதாவது information தேவைப்பட்டால், context உதவும் என்பது நல்ல indication.
Recap
- Context ஒரு component-க்கு அதன் கீழுள்ள முழு tree-க்கும் சில information provide செய்ய அனுமதிக்கிறது.
- Context pass செய்ய:
export const MyContext = createContext(defaultValue)மூலம் அதை create செய்து export செய்யுங்கள்.- எவ்வளவு ஆழத்தில் இருந்தாலும் எந்த child component-இலும் read செய்ய
useContext(MyContext)Hook-க்கு pass செய்யுங்கள். - Parent-இலிருந்து provide செய்ய children-ஐ
<MyContext value={...}>-க்குள் wrap செய்யுங்கள்.
- Context நடுவே உள்ள எந்த components வழியாகவும் pass ஆகும்.
- Context “தங்கள் surroundings-க்கு adapt ஆகும்” components எழுத அனுமதிக்கிறது.
- Context பயன்படுத்துவதற்கு முன், props pass செய்வதையோ JSX-ஐ
childrenஆக pass செய்வதையோ முயற்சிக்கவும்.
Challenge 1 of 1: Prop drilling-ஐ context-ஆல் மாற்றுங்கள்
இந்த example-இல், checkbox toggle செய்வது ஒவ்வொரு <PlaceImage>-க்கும் pass செய்யப்படும் imageSize prop-ஐ மாற்றுகிறது. Checkbox state top-level App component-இல் வைத்திருக்கப்படுகிறது, ஆனால் ஒவ்வொரு <PlaceImage>-க்கும் அது தெரிய வேண்டும்.
தற்போது, App imageSize-ஐ List-க்கு pass செய்கிறது; அது ஒவ்வொரு Place-க்கும் pass செய்கிறது; அது PlaceImage-க்கு pass செய்கிறது. imageSize prop-ஐ remove செய்து, அதற்கு பதிலாக App component-இலிருந்து நேரடியாக PlaceImage-க்கு pass செய்யுங்கள்.
Context.js-இல் context declare செய்யலாம்.
import { useState } from 'react'; import { places } from './data.js'; import { getImageUrl } from './utils.js'; export default function App() { const [isLarge, setIsLarge] = useState(false); const imageSize = isLarge ? 150 : 100; return ( <> <label> <input type="checkbox" checked={isLarge} onChange={e => { setIsLarge(e.target.checked); }} /> பெரிய images பயன்படுத்து </label> <hr /> <List imageSize={imageSize} /> </> ) } function List({ imageSize }) { const listItems = places.map(place => <li key={place.id}> <Place place={place} imageSize={imageSize} /> </li> ); return <ul>{listItems}</ul>; } function Place({ place, imageSize }) { return ( <> <PlaceImage place={place} imageSize={imageSize} /> <p> <b>{place.name}</b> {': ' + place.description} </p> </> ); } function PlaceImage({ place, imageSize }) { return ( <img src={getImageUrl(place)} alt={place.name} width={imageSize} height={imageSize} /> ); }