استخدام السياق (Context) في React

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث

يُزوِّدنا السياق (Context) بطريقة لتمرير البيانات عبر شجرة المُكوّنات بدون الحاجة لتمرير الخاصيّات props يدويًّا من الأعلى للأسفل في كل مستوى.

تُمرَّر البيانات في تطبيقات React الاعتيادية من المستوى الأعلى للأسفل (أي من المكوّنات الآباء إلى المكوّنات الأبناء) عبر الخاصيّات props، ولكن قد يكون هذا بطيئًا لبعض أنواع الخاصيّات (مثل تفضيلات اللغة وقوالب واجهة المستخدم) والتي تحتاجها العديد من المكوّنات ضمن التطبيق. يُزوِّدنا السياق بطريقة لمشاركة القيم كتلك الموجودة بين المكوّنات بدون الاضطرار لتمرير الخاصيّات عبر كل مستوى من الشجرة.

متى نستخدم السياق

يكون السياق مُصمَّمًا لمشاركة البيانات التي تُعتبر عامّة (global) لشجرة مكوّنات React، مثل المستخدم قيد المصادقة حاليًّا، أو القالب، أو تفضيلات اللغة. على سبيل المثال في الشيفرة التالية نُمرِّر خاصيّة القالب (theme) يدويًّا من أجل تنسيق مكوّن الزر Button:

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // يجب أن يأخذ مكون شريط الأدوات Toolbar خاصيّة theme إضافية
  // ويمررها إلى ThemedButton
  // قد يصبح هذا عملًا شاقًّا إن احتاج كل زر في التطبيق أن يعرف القالب theme
  // لأنه يجب تمريره عبر كل المكونات
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

function ThemedButton(props) {
  return <Button theme={props.theme} />;
}

نتجنب باستخدام السياق تمرير الخاصيات عبر عناصر وسيطة:

// يُتيح لنا السياق تمرير القيمة بعمق شجرة المكونات
// بدون الحاجة إلى تمريره إلى كل مكون
// أنشئ السياق لأجل القالب الحالي (مع وضع القيمة light كقيمة افتراضية)
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
	// استخدم Provider لتمرير القالب الحالي إلى الشجرة التالية.
	// يستطيع أي مكون قراءة القالب الحالي بغض النظر عن مدى عمقه في شجرة المكونات
	// في هذا المثال نمرر "dark" كقيمة للقالب الحالي
	
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// لا يجب على المكون الموجود في الوسط أن يمرر القالب للمستويات الأدنى منه
function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton(props) {
  // استخدام Consumer لقراءة سياق القالب الحالي
  // ستبحث React عن أقرب Provider للقالب في المستوى الأعلى منها وتستخدم قيمته
  // في هذا المثال قيمة القالب الحالي هي "dark"
  
  
  return (
    <ThemeContext.Consumer>
      {theme => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

قبل أن تستخدم السياق

يُستخدَم السياق بشكل أساسي عند الحاجة للوصول إلى بعض البيانات من قبل العديد من المكونات في مستويات متداخلة مختلفة، ولكن لا يجب استخدامه بكثرة لأنّ يجعل من إعادة استخدام المكونات أمرًا أكثر صعوبة.

إن أردت فقط تجنّب تمرير بعض الخاصيّات عبر العديد من المستويات، فسيكون استخدام التراكيب حلًّا أبسط من استخدام السياق.

على سبيل المثال افترض وجود مكون للصفحة Page والذي يُمرِّر خاصيّات المستخدم user وحجم الصورة الرمزية avatarSize عبر مستويات عديدة للأسفل بحيث تتمكّن مكونات الرابط Link والصورة الرمزية Avatar الموجودة في مستويات عميقة ومتداخلة أن تقرأها:

<Page user={user} avatarSize={avatarSize} />
// ... والذي يصير ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... والذي يصير ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... والذي يصير ...
<Link href={user.permalink}>
  <Avatar user={user} size={avatarSize} />
</Link>

قد يبدو من الفائض تمرير الخاصيّات user و avatarSize عبر مستويات عديدة للأسفل إن كان يحتاجه في النهاية فقط المكوّن Avatar. من المزعج أيضًا كلّما احتاج المُكوِّن Avatar المزيد من الخاصيّات من المستويات الأعلى فيجب عليك إضافتها في كل المستويات الوسيطة أيضًا.

من طرق حل هذه المشكلة بدون السياق هي تمرير المكون Avatar نفسه للأسفل بحيث لا تحتاج المكوّنات الوسيطة أن تعلم حول الخاصيّة user: