useContext הוא React Hook המאפשר לך לקרוא ולהירשם ל-context מהרכיב שלך.
const value = useContext(SomeContext)הפניה
useContext(SomeContext)
התקשר ל-useContext ברמה העליונה של הרכיב שלך כדי לקרוא ולהירשם ל-context.
import { useContext } from 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...פרמטרים
SomeContext: ההקשר שיצרת בעבר עםcreateContext. ההקשר עצמו אינו מחזיק את המידע, הוא רק מייצג את סוג המידע שאתה יכול לספק או לקרוא ממרכיבים.
מחזירה
useContext מחזיר את ערך ההקשר עבור הרכיב המתקשר. זה נקבע כשה-value עובר ל-SomeContext.Provider הקרוב ביותר מעל רכיב הקורא בעץ. אם אין ספק כזה, אז הערך המוחזר יהיה ה-defaultValue שהעברת ל-createContext עבור ההקשר הזה. הערך המוחזר תמיד מעודכן. React מעבד מחדש אוטומטית רכיבים שקוראים הקשר כלשהו אם הוא משתנה.
אזהרות
useContext()קריאה ברכיב לא מושפעת מספקים שהוחזרו מאותו רכיב. ה-<Context.Provider>התואם צריך להיות מעל לרכיב המבצע את הקריאהuseContext().- React מציג מחדש באופן אוטומטי את כל הילדים שuse הקשר מסוים החל מהספק שמקבל
valueאחר. הערכים הקודמים והבאים מושווים עם ההשוואהObject.is. דילוג על עיבוד מחדש עםmemoאינו מונע מהילדים לקבל ערכי הקשר טריים. - אם מערכת הבנייה שלך מייצרת מודולים כפולים בפלט (מה שיכול לקרות עם קישורים סימליים), זה יכול לשבור את ההקשר. העברת משהו דרך הקשר עובד רק אם
SomeContextשאתה use לספק הקשר וSomeContextשאתה use כדי לקרוא אותו הם בדיוק אותו אובייקט, כפי שנקבע על ידי השוואה===.
שימוש
העברת נתונים עמוק לתוך העץ
התקשר ל-useContext ברמה העליונה של הרכיב שלך כדי לקרוא ולהירשם ל-context.
import { useContext } from 'react';
function Button() {
const theme = useContext(ThemeContext);
// ...useContext מחזירה את ערך ההקשר עבור context שעברת. כדי לקבוע את ערך ההקשר, React מחפש בעץ הרכיבים ומוצא את ספק ההקשר הקרוב ביותר לעיל עבור ההקשר המסוים הזה.
כדי להעביר הקשר ל-Button, עטוף אותו או אחד ממרכיבי האב שלו לספק ההקשר המתאים:
function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}
function Form() {
// ... renders buttons inside ...
}זה לא משנה כמה שכבות של רכיבים יש בין הספק ל-Button. כאשר Button בכל מקום בתוך Form קורא useContext(ThemeContext), הוא יקבל "dark" כערך.
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
עדכון נתונים שהועברו באמצעות ההקשר
לעתים קרובות, תרצה שההקשר ישתנה עם הזמן. כדי לעדכן את ההקשר, שלב אותו עם state. הכריז על משתנה state ברכיב האב, והעביר את ה-state הנוכחי בתור ערך ההקשר לספק.
function MyPage() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<Form />
<Button onClick={() => {
setTheme('light');
}}>
Switch to light theme
</Button>
</ThemeContext.Provider>
);
}כעת כל Button בתוך הספק יקבל את הערך הנוכחי theme. אם תתקשר ל-setTheme כדי לעדכן את הערך theme שתעביר לספק, כל רכיבי Button יעבדו מחדש עם הערך החדש 'light'.
Example 1 of 5: עדכון ערך באמצעות הקשר
בדוגמה זו, הרכיב MyApp מחזיק במשתנה state אשר מועבר לאחר מכן לספק ThemeContext. סימון תיבת הסימון “מצב כהה” מעדכן את ה-state. שינוי הערך שסופק מעבד מחדש את כל הרכיבים תוך שימוש בהקשר זה.
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <ThemeContext.Provider value={theme}> <Form /> <label> <input type="checkbox" checked={theme === 'dark'} onChange={(e) => { setTheme(e.target.checked ? 'dark' : 'light') }} /> Use dark mode </label> </ThemeContext.Provider> ) } function Form({ children }) { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
שימו לב שvalue="dark" מעביר את המחרוזת "dark", אבל value={theme} מעביר את הערך של המשתנה JavaScript theme עם JSX סוגרים מסולסלים. תוספים מסולסלים לא מאפשרים לך להעביר גם סוגרים מסולסלים.
ציון ערך ברירת המחדל
אם React לא יכול למצוא ספקים של ההקשר הספציפי הזה בעץ האב, ערך ההקשר המוחזר על ידי useContext() יהיה שווה לערך ברירת המחדל שציינת כאשר [יצרת/י את ההקשר הזה]):(/reacted).
const ThemeContext = createContext(null);ערך ברירת המחדל לעולם לא משתנה. אם אתה רוצה לעדכן את ההקשר, use אותו עם state כפי מתואר לעיל.
לעתים קרובות, במקום null, יש ערך משמעותי יותר שאתה יכול use כברירת מחדל, לדוגמה:
const ThemeContext = createContext('light');בדרך זו, אם תעבד בטעות רכיב כלשהו ללא ספק מתאים, הוא לא יישבר. זה גם עוזר לרכיבים שלך לעבוד היטב בסביבת בדיקה מבלי להגדיר הרבה ספקים בבדיקות.
בדוגמה שלמטה, כפתור “החלפת נושא” תמיד בהיר כיuse הוא מחוץ לכל ספק הקשר ערכת נושא וערך ערכת נושא ההקשר המוגדר כברירת מחדל הוא 'light'. נסה לערוך את ערכת הנושא המוגדרת כברירת מחדל להיות 'dark'.
import { createContext, useContext, useState } from 'react'; const ThemeContext = createContext('light'); export default function MyApp() { const [theme, setTheme] = useState('light'); return ( <> <ThemeContext.Provider value={theme}> <Form /> </ThemeContext.Provider> <Button onClick={() => { setTheme(theme === 'dark' ? 'light' : 'dark'); }}> Toggle theme </Button> </> ) } function Form({ children }) { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children, onClick }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className} onClick={onClick}> {children} </button> ); }
עקיפת הקשר עבור חלק מהעץ
אתה יכול לעקוף את ההקשר של חלק מהעץ על ידי עטיפה של חלק זה בספק בעל ערך שונה.
<ThemeContext.Provider value="dark">
...
<ThemeContext.Provider value="light">
<Footer />
</ThemeContext.Provider>
...
</ThemeContext.Provider>אתה יכול לקנן ולעקוף ספקים כמה פעמים שאתה צריך.
Example 1 of 2: עקיפה של ערכת נושא
כאן, הכפתור בתוך ה-Footer מקבל ערך הקשר שונה ("light") מהכפתורים שבחוץ ("dark").
import { createContext, useContext } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> <ThemeContext.Provider value="light"> <Footer /> </ThemeContext.Provider> </Panel> ); } function Footer() { return ( <footer> <Button>Settings</Button> </footer> ); } function Panel({ title, children }) { const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> {title && <h1>{title}</h1>} {children} </section> ) } function Button({ children }) { const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
אופטימיזציה של עיבוד מחדש בעת העברת אובייקטים ופונקציות
אתה יכול להעביר כל ערכים דרך הקשר, כולל אובייקטים ופונקציות.
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
function login(response) {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}
return (
<AuthContext.Provider value={{ currentUser, login }}>
<Page />
</AuthContext.Provider>
);
}כאן, ערך ההקשר הוא אובייקט JavaScript עם שני מאפיינים, שאחד מהם הוא פונקציה. בכל פעם ש-MyApp מעבד מחדש (לדוגמה, בעדכון מסלול), זה יהיה אובייקט שונה המצביע על פונקציה שונה, כך שגם React יצטרך לעבד מחדש את כל הרכיבים עמוק בעץ שקוראים ל-useContext(AuthContext).
באפליקציות קטנות יותר, זו לא בעיה. עם זאת, אין צורך לעבד אותם מחדש אם הנתונים הבסיסיים, כמו currentUser, לא השתנו. כדי לעזור ל-React לנצל את העובדה הזו, תוכלו לעטוף את הפונקציה login ב-useCallback ולעטוף את יצירת האובייקט ב-useMemo. זוהי אופטימיזציה של ביצועים:
import { useCallback, useMemo } from 'react';
function MyApp() {
const [currentUser, setCurrentUser] = useState(null);
const login = useCallback((response) => {
storeCredentials(response.credentials);
setCurrentUser(response.user);
}, []);
const contextValue = useMemo(() => ({
currentUser,
login
}), [currentUser, login]);
return (
<AuthContext.Provider value={contextValue}>
<Page />
</AuthContext.Provider>
);
}כתוצאה משינוי זה, גם אם MyApp צריך לבצע רינדור מחדש, הרכיבים הקוראים useContext(AuthContext) לא יצטרכו לבצע רינדור מחדש אלא אם כן currentUser השתנה.
קרא עוד על useMemo ועל useCallback.
פתרון בעיות
הרכיב שלי לא רואה את הערך מהספק שלי
ישנן כמה דרכים נפוצות שבהן זה יכול לקרות:
- אתה מעבד את
<SomeContext.Provider>באותו רכיב (או מתחת) שבו אתה קורא ל-useContext(). העבר את<SomeContext.Provider>מעל ומחוץ את הרכיב הקוראuseContext(). - ייתכן ששכחת לעטוף את הרכיב שלך ב-
<SomeContext.Provider>, או ששמת אותו בחלק אחר של העץ ממה שחשבתם. בדוק אם ההיררכיה נכונה באמצעות React DevTools. - ייתכן שנתקלת בבעיית בנייה כלשהי בכלי העבודה שלך שuses
SomeContextכפי שנראה מהרכיב המספק וSomeContextכפי שנראה על ידי רכיב הקריאה כשני אובייקטים שונים. זה יכול לקרות אם אתה use קישורים סימליים, למשל. אתה יכול לאמת זאת על ידי הקצאתם לגלובלים כמוwindow.SomeContext1ו-window.SomeContext2ולאחר מכן בדיקה אםwindow.SomeContext1 === window.SomeContext2במסוף. אם הם לא זהים, תקן את הבעיה ברמת כלי הבנייה.
אני תמיד מקבל undefined מההקשר שלי למרות שערך ברירת המחדל שונה
יכול להיות שיש לך ספק בלי value בעץ:
// 🚩 Doesn't work: no value prop
<ThemeContext.Provider>
<Button />
</ThemeContext.Provider>אם תשכח לציין value, זה כמו להעביר את value={undefined}.
ייתכן שבטעות useתת שם אבזר אחר בטעות:
// 🚩 Doesn't work: prop should be called "value"
<ThemeContext.Provider theme={theme}>
<Button />
</ThemeContext.Provider>בשני המקרים הללו אתה אמור לראות אזהרה מ-React במסוף. כדי לתקן אותם, קרא לאביזר value:
// ✅ Passing the value prop
<ThemeContext.Provider value={theme}>
<Button />
</ThemeContext.Provider>שים לב ש-ערך ברירת המחדל מהקריאה createContext(defaultValue) שלך הוא רק used אם אין ספק תואם למעלה בכלל. אם יש רכיב <SomeContext.Provider value={undefined}> איפשהו בעץ האב, הרכיב שקורא את הערך __TK___ יקבל את הערך __TK___2_K.