في React، يعد السياق وسيلة لتمرير البيانات بين أشجار المكونات دون إضافة الدعائم يدويًا لكل طبقة من المكونات؛ ويوفر السياق طريقة لمشاركة القيم المحددة بين المكونات دون الحاجة إلى تمريرها بشكل صريح عبر المكون في الشجرة.
بيئة تشغيل هذا البرنامج التعليمي: نظام Windows 10، إصدار التفاعل 17.0.1، كمبيوتر Dell G3.
يوفر السياق طريقة لتمرير البيانات بين أشجار المكونات دون إضافة الدعائم يدويًا إلى كل طبقة من المكونات. في تطبيق React النموذجي، يتم تمرير البيانات من أعلى إلى أسفل (من الأصل إلى الابن) من خلال الدعائم، ولكن هذا الأسلوب مرهق للغاية بالنسبة لأنواع معينة من الخصائص (مثل تفضيلات الإعدادات المحلية، وموضوع واجهة المستخدم)، وهذه الخصائص مطلوبة من قبل العديد من المكونات في التطبيق. يوفر السياق طريقة لمشاركة هذه القيم بين المكونات دون الحاجة إلى تمرير الخاصيات بشكل صريح عبر كل مستوى من مستويات شجرة المكونات.
تم تصميم السياق لمشاركة البيانات "الشاملة" في شجرة المكونات، مثل المستخدم المعتمد حاليًا أو السمة أو اللغة المفضلة. على سبيل المثال، في التعليمة البرمجية التالية، نقوم يدويًا بضبط نمط مكون الزر من خلال سمة "theme".
class App Extends React.Component { render() { return <Toolbar theme="dark" /> }}function Toolbar(props) { // يقبل مكون شريط الأدوات خاصية "السمة" الإضافية، والتي يتم تمريرها بعد ذلك إلى ThemedButton. عنصر. // سيكون الأمر مزعجًا إذا كان كل زر في التطبيق يحتاج إلى معرفة قيمة السمة، // لأنه يجب تمرير هذه القيمة إلى جميع المكونات. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton يمتد React.Component { render() { return <Button theme={this.props.theme} /> }; }// تمرير الدعائم: التطبيق -> شريط الأدوات -> ThemedButton// إذا كان التداخل عميقًا جدًا، فيجب تمرير الدعائم طبقة تلو الأخرى، حتى لو لم تكن هناك حاجة إلى الدعائم في المنتصف، يبدو الأمر مرهقًا للغاية.باستخدام السياق، يمكننا تجنب تمرير الدعائم من خلال العناصر الوسيطة
// يسمح لنا السياق بتمرير القيم بعمق في شجرة المكونات دون الحاجة إلى تمريرها بشكل صريح عبر كل مكون. // أنشئ سياقًا للموضوع الحالي ("الضوء" هو القيمة الافتراضية). const ThemeContext = React.createContext('light');class App Extends React.Component { render() { // استخدم موفرًا لتمرير السمة الحالية إلى شجرة المكونات التالية. // بغض النظر عن مدى عمقها، يمكن لأي مكون قراءة هذه القيمة. // في هذا المثال، نقوم بتمرير "dark" كقيمة حالية. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> }}// لم يعد المكون الوسيط بحاجة إلى تحديد السمة المراد تمريرها. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton يمتد React.Component { // حدد contextType لقراءة سياق السمة الحالية. // سوف تجد React أقرب مزود للموضوع وتستخدم قيمته. // في هذا المثال، قيمة السمة الحالية هي "مظلمة". static contextType = ThemeContext; render() { return <Button theme={this.context} /> }}// يمكنك أيضًا استخدام ThemedButto.contextType = ThemeContext;إنشاء كائن سياق. عندما تعرض React مكونًا مشتركًا في كائن السياق هذا، سيقرأ المكون قيمة السياق الحالية من الموفر المطابق الأقرب إليه في شجرة المكونات.
فقط في حالة عدم وجود موفر مطابق في الشجرة التي يوجد بها المكون، سيتم تفعيل معلمة القيمة الافتراضية الخاصة به. يساعد هذا في اختبار المكونات دون استخدام موفر لتغليف المكون. ملاحظة: عندما يتم تمرير قيمة غير محددة إلى قيمة الموفر، فإن القيمة الافتراضية للمكون المستهلك لن تصبح سارية المفعول.
يُرجع كل كائن سياق مكون Provider React، والذي يسمح للمكونات المستهلكة بالاشتراك في تغييرات السياق.
يتلقى الموفر سمة القيمة ويمررها إلى المكون المستهلك. يمكن أن يكون للموفر علاقة مقابلة مع مكونات المستهلك المتعددة. يمكن أيضًا استخدام موفري خدمات متعددين متداخلين، وستقوم الطبقة الداخلية بالكتابة فوق بيانات الطبقة الخارجية.
عندما تتغير قيمة الموفر، سيتم إعادة عرض جميع المكونات المستهلكة بداخله. لا يخضع الموفر ولا مكونات المستهلك الداخلية الخاصة به إلى وظيفة mustComponentUpdate، لذلك يمكن تحديث مكون المستهلك حتى إذا خرج المكون الأصلي الخاص به من التحديث.
سيتم إعادة تعيين سمة contextType المثبتة على الفصل إلى كائن سياق تم إنشاؤه بواسطة React.createContext(). يتيح لك هذا استخدام this.context لاستهلاك القيمة في السياق الأحدث. يمكنك الوصول إليه في أي دورة حياة، بما في ذلك وظيفة العرض
import MyContext from './MyContext';class MyClass Extends React.Component {componentDidMount() { Let value = this.context /* بعد تثبيت المكون، استخدم قيمة مكون MyContext لتنفيذ بعض عمليات التأثير الجانبي* / }componentDidUpdate() { Let value = this.context; /* ... */ }componentWillUnmount() { Let value = this.context; ; /* يتم العرض بناءً على قيمة مكون MyContext*/ } // أو استخدم static contextType = MyContext;}MyClass.contextType = MyContext;هنا، يمكن لمكونات React أيضًا الاشتراك في تغييرات السياق. يتيح لك هذا الاشتراك في السياق في المكونات الوظيفية.
وهذا يتطلب وظيفة كطفل. تستقبل هذه الوظيفة قيمة السياق الحالية وترجع عقدة React. القيمة التي تم تمريرها إلى الدالة تعادل القيمة المقدمة من قبل الموفر الأقرب إلى هذا السياق أعلى شجرة المكونات. إذا لم يكن هناك موفر مقابل، فإن معلمة القيمة تعادل القيمة الافتراضية التي تم تمريرها إلى createContext().
يقبل كائن السياق خاصية تسمى DisplayName، وهي من النوع string. يستخدم React DevTools هذه السلسلة لتحديد السياق الذي سيتم عرضه.
سيظهر المكون التالي باسم MyDisplayName في DevTools
const MyContext = React.createContext(/* some value */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" في DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" في أدوات التطويربالنسبة لمثال الموضوع أعلاه، يمكن تحقيق استخدام أكثر تعقيدًا باستخدام القيم الديناميكية.
theme-context.js
تصدير سمات const = { فاتح: { المقدمة: '#000000'، الخلفية: '#eeeeee'، }، داكن: { المقدمة: '#ffffff'، الخلفية: '#222222'، }،}؛ تصدير const ThemeContext = React .createContext(themes.dark); // هذه هي القيمة الافتراضيةtheme-button.js
import { ThemeContext } from './theme-context'; class ThemedButton Extends React.Component { render() { Letprops = this.props; {...props} style={{backgroundColor: theme.background}} /> } // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;export default ThemedButton;app.js
import { ThemeContext, theme } from './theme-context';import ThemedButton from './themed-button';// وظيفة مكون وسيطة تستخدم ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > تغيير السمة </ThemedButton> );} class App Extends React.Component { buildor(props) { super(props); this.state = { theme: theme.light, }; this.toggleTheme = () => { this .setState(state => ({ theme:state.theme === theme.dark ? theme.light : theme.dark, })); render() { // يُستخدم بواسطة مكون زر ThemedButton داخل ThemeProvider The theme القيمة في الحالة، // وتستخدم المكونات الخارجية قيمة السمة الافتراضية return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar ChangeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> }}ReactDOM.render(<App />, document.root);// يمكن للمكونات الملتفة بواسطة ThemeContext.Provider أن تستهلك قيمة ThemeContext// في شريط الأدوات ويمكن لـ ThemedButton استخدام this.context للحصول على القيمة // لاحظ أن طريقة تحديث الحالة هي تمريرها عبر الخاصيات، وسيتم تشغيل التحديث بواسطة المكونات التابعة، وستتم مناقشة الطريقة من خلال السياق أدناهفي المثال أعلاه، نقوم بتمرير وظيفة التحديث للأسفل من خلال الدعائم لتغيير قيمة السمات في التطبيق. نحن نعلم أنه من الضروري تحديث السياق من مكون متداخل بعمق في شجرة المكونات. في هذا السيناريو، يمكنك تمرير دالة عبر السياق لجعل مكون المستهلكين يقوم بتحديث السياق.
theme-context.js
// تأكد من مطابقة بنية بيانات القيمة الافتراضية التي تم تمريرها إلى createContext مع مكونات الاتصال (المستهلكين)! Export const ThemeContext = React.createContext({ theme: theme.dark, toggleTheme: () => {}, // تحديد طريقة تحديث السمة وتمريرها});theme-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // لا يحصل زر تبديل السمة على قيمة السمة فحسب، بل يحصل أيضًا على وظيفة تبديل السمة من السياق (جزء app.js أدناه) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> تبديل السمة </button> )} </ThemeContext.Consumer> ); }تصدير ThemeTogglerButton الافتراضي؛app.js
استيراد { ThemeContext، السمات } من './theme-context'؛ استيراد ThemeTogglerButton من './theme-toggler-button'؛ يمتد تطبيق الطبقة React.Component { buildor(props) { super(props); ) => { this.setState(state => ({ theme:state.theme === theme.dark ? theme.light : theme.dark, })); // تحتوي الحالة أيضًا على وظيفة التحديث سيتم تمريرها إلى موفر السياق. this.state = { theme: theme.light, toggleTheme: this.toggleTheme, // تحديد وظيفة التحديث وتمريرها عبر السياق }; render() { // يتم تمرير الحالة بأكملها إلى الموفر return ( < ThemeContext. value={this.state}> <Content /> </ThemeContext.Provider> }}function Content() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <التطبيق />، document.root)؛من أجل ضمان إعادة تصيير السياق بسرعة، تحتاج React إلى جعل سياق كل مكون مستهلك عقدة منفصلة في شجرة المكونات.
// سياق السمة، السمة الافتراضية هي القيمة "الخفيفة" const ThemeContext = React.createContext('light'); // سياق تسجيل دخول المستخدم const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const {signInUser, theme } = this.props; // مكون التطبيق الذي يوفر قيمة السياق الأولية return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < Layout /> </UserContext.Provider> </ThemeContext.Provider> }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// مكون قد تستهلك وظائف سياق متعددة Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext المستهلك> )} </ThemeContext.Consumer> );}إذا تم استخدام قيمتين أو أكثر من قيم السياق معًا بشكل متكرر، فقد ترغب في التفكير في إنشاء مكون التجسيد الخاص بك لتوفير هذه القيم.
نظرًا لأن السياق يستخدم هوية مرجعية لتحديد وقت العرض، فقد تكون هناك بعض الأخطاء التي قد تؤدي إلى عرض غير متوقع في مكون المستهلكين عند إعادة عرض المكون الأصلي للموفر. على سبيل المثال، في كل مرة يتم فيها إعادة عرض الموفر، ستعيد التعليمة البرمجية التالية عرض جميع مكونات المستهلك التالية، لأن سمة القيمة يتم تعيينها دائمًا لكائن جديد
class App Extends React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }});من أجل منع هذا الموقف، يتم ترقية حالة القيمة إلى حالة العقدة الأصلية.
class App Extends React.Component { buildor(props) { super(props); // بعد عمليات العرض المتعددة، سيتم الاحتفاظ بالحالة عندما لا تتغير القيمة، فلن تقوم مكونات المستهلكين التالية بإعادة عرض this.state = {. القيمة: { شيء: 'شيء ما'}، }؛ } render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> }});