فصل ضمیمه: آشنایی با useContext

Appendix: useContext

22 بهمن 1399
فصل ضمیمه: آشنایی با useContext

در جلسات قبل با hook های useReducer و useEffect و useCallback و useState آشنا شدیم و به قدرت آن ها پی بردیم. امروز نوبت به یکی دیگر از hook های مهم react می رسد: useContext (یک API به نام Context). اگر دقت کرده باشید تا به حال از فایل Auth.js استفاده نکرده ایم. این فایل فقط دارای یک دکمه Login است اما فرمی برای Login ندارد. من می خواهم اگر کاربر login نشده بود این صفحه را به او نشان دهم و اگر login شده بود صفحه ingredients را ببیند. در حال حاضر فایل App.js همیشه صفحه ingredients را برمی گرداند:

const App = props => {
  return <Ingredients />;
};

هدف ما این است که این مسئله را تغییر دهیم. بنابراین در همین فایل App.js ابتدا Auth.js را import می کنیم:

import Auth from './components/Auth';

حالا اگر همیشه Auth را برگردانیم:

const App = props => {
  return <Auth />;
};

همیشه صفحه اخطار را می بینیم که به ما می گوید وارد برنامه مان نشده ایم. بنابراین نشان دادن صفحه Auth یا Ingredients باید بر اساس یک شرط خاص باشد (login بودن). ما می توانیم در همین فایل App.js منطق شرطی برای نمایش فایل ها را پیاده سازی کنیم اما در برنامه های پیچیده و واقعی، login بودن کاربر ممکن است به چندین کامپوننت وابسته باشد و پاس دادن این داده ها از چندین کامپوننت به App.js کار طاقت فرسایی خواهد بود (باید از زنجیره های بسیار طولانی props استفاده کنیم). برای حل این مشکل از Context استفاده می کنیم.

یک پوشه جدید در root directory خود به نام Context بسازید که حاوی فایلی به نام auth-context.js باشد. حالا این فایل را باز کرده و مثل من پیش بروید:

import React, { useState } from 'react';

حالا می توانیم یک context بسازیم:

export const AuthContext = React.createContext({
  isAuth: false,
  login: () => {}
});

کد بالا می گوید در حالت پیش فرض ما login نشده ایم (false بودن مقدار isAuth). مقدار Login نیز یک تابع خالی است که برای دریافت auto-completion از ویرایشگر کد خودم نوشته ام و بعدا آن را با یک مقدار واقعی جایگزین می کنیم. ما از همین شیء استفاده می کنیم تا در همین فایل یک کامپوننت دیگر نیز بسازیم:

const AuthContextProvider = props => {

};

حالا با استفاده از Provider یک کامپوننت را تعریف می کنیم که هر چیزی که دریافت می کند را درون خودش نمایش می دهد:

const AuthContextProvider = props => {
  return (
    <AuthContext.Provider>
      {props.children}
    </AuthContext.Provider>
  );
};

این کد می گوید هر prop ای که به آن پاس داده می شود را درون خودت نمایش بده. حالا خارج از قسمت JSX باید state مربوط به Login را مدیریت کنیم بنابراین به useState نیاز خواهیم داشت:

import React, { useState } from 'react';

حالا درون همین کامپوننت state اولیه را روی false می گذاریم تا در حالت پیش فرض هیچ کس login نشده باشد:

  const [isAuthenticated, setIsAuthenticated] = useState(false);

در مرحله بعد به متدی نیاز داریم که کاربر ما را login کند. من این متد را به شکل زیر تعریف کرده ام:

  const loginHandler = () => {
    setIsAuthenticated(true);
  };

من به سادگی از تابع useState استفاده کرده ام تا وضعیت login بودن کاربر را روی true تنظیم کنم. سپس AuthContext.Provider نیز یک prop به نام value را می گیرد که بین همه کامپوننت های مربوط به پخش می شود. ما context خود را درون آن می گذاریم تا اگر تغییر کرد، تمام کامپوننت های مورد نظر دیگر متوجه بشوند. ما بالاتر با استفاده از useContext یک ثابت به نام AuthContext تعریف کردیم که context ما را در خود دارد اما value باید این مقدار را overwrite کند.

  return (
    <AuthContext.Provider
      value={{ login: loginHandler, isAuth: isAuthenticated }}
    >
      {props.children}
    </AuthContext.Provider>
  );

با این کار دو مقدار پیش فرض Login و isAuth درون context را overwrite می کنیم. در نهایت این کامپوننت را نیز export می کنیم:

export default AuthContextProvider;

نمایی کلی از کدهای این کامپوننت:

import React, { useState } from 'react';

export const AuthContext = React.createContext({
  isAuth: false,
  login: () => {}
});

const AuthContextProvider = props => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const loginHandler = () => {
    setIsAuthenticated(true);
  };

  return (
    <AuthContext.Provider
      value={{ login: loginHandler, isAuth: isAuthenticated }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContextProvider;

در مرحله بعد برای استفاده از این کامپوننت باید به فایل index.js برویم و کامپوننت را در آن import کنیم:

import AuthContextProvider from './context/auth-context';

سپس تمام برنامه خودمان را با context می پوشانیم:

ReactDOM.render(
  <AuthContextProvider>
    <App />
  </AuthContextProvider>,
  document.getElementById('root')
);

با این کار برنامه ما می تواند context را دریافت کند. یعنی هر کامپوننتی از هر جایی به context گوش می دهد و از تغییرات آن مطلع می شود. در نهایت به فایل App.js برگردید تا منطق شرطی آن را کامل کنیم. ابتدا AuthContext را وارد این فایل می کنیم:

import { AuthContext } from './context/auth-context';

توجه داشته باشید که کامپوننت provider را وارد نکرده ایم بلکه خود context را که AuthContext نام داشت وارد فایل کرده ایم. اگر یادتان نمی آید که Context چه چیزی بود بهتر است در روکسو یک سرچ ساده در این مورد انجام دهید. من این مسائل را در این قسمت و این قسمت از دوره react توضیح داده ام.

در مرحله بعد باید از useContext استفاده کنیم. باید این hook را صدا بزنید و context مورد نظر خود را که می خواهید به آن گوش دهید، به useContext پاس بدهید. گوش دادن یعنی زیر نظر گرفتن تغییرات یک واحد خاص (مثلا context). بنابراین می گوییم:

const App = props => {
  const authContext = useContext(AuthContext);

  let content = <Auth />;
  if (authContext.isAuth) {
    content = <Ingredients />;
  }

  return content;
};

یعنی authContext (ثابتی که با const تعریف کرده ایم) یک اشاره یا handle به AuthContext اصلی ما است و می توانیم برای دسترسی داشتن به AuthContext از آن استفاده کنیم. همچنین از آنجایی که از useContext استفاده کرده ایم، این کامپوننت در حال گوش دادن به AuthContext است بنابراین هر زمانی که AuthContext تغییر کند (کاربر login شده یا logout شود) این کامپوننت هم دوباره ساخته می شود تا از این تغییرات مطلع شویم. به همین دلیل می توانیم با یک شرط ساده authContext.isAuth را چک کنیم تا اگر صحیح بود (کاربر login بود) صفحه Ingredients را نمایش دهند.

در مرحله آخر باید کاری کنیم که کاربر با کلیک روی دکمه Login (در صفحه خطایی که مربوط به Login شدن است) حتما به متد loginHandler برسد (در فایل Auth-context.js). ما می دانیم که context درون value خود یک اشاره به این متد دارد:

  return (
    <AuthContext.Provider
      value={{ login: loginHandler, isAuth: isAuthenticated }}
    >
      {props.children}
    </AuthContext.Provider>
  );

بنابراین به فایل Auth.js می رویم و آنجا AuthContext را به همراه useContext وارد کنیم:

import { AuthContext } from '../context/auth-context';

import React, { useContext } from 'react';

در مرحله بعد وارد کامپوننت Auth می شویم و یک handle برای AuthContext می سازیم. Handle یعنی متغیری که به ما اجازه می دهد به AuthContext دسترسی داشته باشیم:

const Auth = props => {
  const authContext = useContext(AuthContext);

و نهایتا متد Login را از آن صدا می زنیم:

const Auth = props => {
  const authContext = useContext(AuthContext);

  const loginHandler = () => {
    authContext.login();
  };
// بقیه کدها //

حالا می توانید این کدها را در مرورگر خود تست کنید. باید بدون هیچ اشکالی برایتان اجرا شوند. باز هم می گویم که اگر یادتان نمی آید Context چیست به این قسمت و این قسمت مراجعه کنید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.