در این مقاله یکی از سناریوهای رایج استفاده از Context API را مورد بررسی قرار می دهیم. می خواهیم با استفاده از Context API تم اپلیکیشن را تغییر دهیم. در رابطه با مفهوم prop drilling و Context API در مقاله "مقدمه ای بر مفاهیم prop drilling و Context API" مفصل صحبت کرده ایم. در این مقاله خیلی خلاصه مفهوم Context API رو توضیح می دهیم و بلافاصله پیاده سازی اپلیکیشن مدیریت تم را شروع می کنیم.
همچنین برای آموزش های بیشتر در زمینه ری اکت به بخش آموزش React مراجعه کنید.
Context API
Context API روش ارسال دیتا در یک درخت از کامپوننت ها بدون استفاده از props است. به عبارت بهتر، Context API یک مسئله ساده را مورد هدف قرار می دهد: چگونه مدیریت state را در چند سطح از کامپوننت ها مدیریت کنیم در صورتی که بخواهیم بدون ارسال داده ها از ریشه درخت کامپوننت به پایین سطح فرزندان، این ارسال انجام شود؟
Context API در اساس از سه بخش اصلی تشکیل شده است:
- Context Object
- Context Provider
- Context Consumer
Context Object را از طریق کد زیر تعریف می کنیم:
Const Context = createContext();
پس از تعریف Context لازم است Provider تعریف شود. Provider در واقع خصوصیتی از Context است و به شکل زیر تعریف می شود:
<Context.Provider value={value}>
{/* Children */}
</Context.Provider>;
نکته مهم اینکه همه کامپوننت هایی که قرار است به دیتای مشترک دسترسی داشته باشند باید به عنوان زیر کامپوننت Provider تعریف شود.
پس از تعریف Provider لازم است کامپوننت هایی که قرار است به دیتای مشترک دسترسی داشته باشند با استفاده از متد useCotnext به context object اصطلاحا subscribe کنند. زمانی که یک کامپوننت به context object، subscribe می کند آماده است تا مقدار context که از طریق provider تولید می شود را دریافت کند. پس از اینکه provider مقدار خصوصیت value را تغییر می دهد این تغییرات به تمامی کامپوننت هایی که از طریق useContext عمل subscription را انجام داده اند ارسال می شود و این کامپوننت ها با مقدار تازه دریافت شده آپدیت می شوند.
پیاده سازی اپلیکیشن
ابتدا در فولدر public یک فولدر جدید به نام css تعریف کنید و Url زیر را در browser وارد کنید:
https://course-sources.s3.ir-thr-at1.arvanstorage.com/theme.min.css
و فایل stylesheet ی که در مرورگر نمایش داده می شود را دانلود و به فولدر css منتقل کنید. فایل index.html را باز کنید و در بخش head لینک به فایل stylesheet را به شکل زیر تعریف کنید:
<link rel="stylesheet" href="css/theme.min.css">
در فولدر src یک فایل به نام context.js تعریف کنید. در این فایل Context Object و Context Provider را تعریف می کنیم. کد زیر را در این فایل تعریف کنید:
const { createContext, useState, useContext } = require("react");
const ThemeContext = createContext();
export const ThemeProvider = ({children}) => {
const [darkMode, setDarkMode] = useState(false);
return <ThemeContext.Provider value={{darkMode, setDarkMode}}>
{children}
</ThemeContext.Provider>
}
export const useThemeContext = () => {
return useContext(ThemeContext);
}
در ابتدا یک Context جدید تعریف کرده ایم و آن را به متغیر ThemeContext منتسب کرده ایم. پس از تعریف Context تابع ThemeProvider را تعریف کرده ایم که از طریق children ، props را به عنوان پارامتر دریافت می کند. یک state به نام darkMode با مقدار اولیه false تعریف کرده ایم. فرض ما این است که تم اولیه روشن است. و سپس کامپوننت Provider را از طریق ThemeContext تعریف کرده ایم. children را که در حقیقت کامپوننت های زیر مجموعه هستند به عنوان فرزند کامپوننت Provider تعریف کرده ایم. و نکته مهمتر اینکه خصوصیت value که مقدار provider را مشخص می کند با آبجکتی با این خصوصیات تعریف کرده ایم: darkMode و setDarkMode. در حقیقت با تغییر تم، darkMode تغییر می کند و تمامی کامپوننت های داخلی متوجه تغییر می شوند و آپدیت می شوند. همچنین برای تغییر تم لازم است setDarkMode را هم ارسال کنیم تا کامپوننت های داخلی بتوانند تم را تغییر دهند. در آخر با استفاده از یک Custom Hook به نام useThemeContext متد useContext را با مقدار ThemeContext بر می گردانیم. علت این کار جلوگیری از تکرار useContext در کامپوننت های داخلی است.
فایل Index.js را باز کنید و کامپوننت ThemeProvider را تعریف کنید و app component را به عنوان فرزند ThemeProvider در نظر بگیرید:
import { ThemeProvider } from "./context";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<ThemeProvider>
<App />
</ThemeProvider>
</React.StrictMode>
);
در فولدر src، فولدری به نام components تعریف کنید و فایل header.jsx را به آن اضافه کنید. کد زیر را به این کامپوننت اضافه کنید:
import { useThemeContext } from "../context";
const Header = () => {
const theme = useThemeContext();
const darkMode = theme.darkMode;
const changeTheme = () => {
theme.setDarkMode(!darkMode);
}
return (
<header
className={`navbar navbar-expand ${
darkMode ? "navbar-dark bg-dark" : "bg-secondary navbar-light"
} shadow-sm`}
>
<div className="container">
<ul className="navbar-nav">
<li className="navbar-item">
<a className="nav-link">صفحه اصلی</a>
</li>
<li className="navbar-item">
<a className="nav-link">محصولات ما</a>
</li>
<li className="navbar-item">
<a className="nav-link">درباره ما</a>
</li>
<li className="navbar-item">
<a className="nav-link">ارتباط با ما</a>
</li>
</ul>
<button onClick={changeTheme} className={`btn ${darkMode ? 'btn-light' : 'btn-dark'} `}>
{darkMode ? 'روشن': 'تاریک'}
</button>
</div>
</header>
);
};
export default Header;
نکته مهم در این کامپوننت استفاده از useThemeContext است. با این کار در واقع کامپوننت Header را به عنوان مصرف کننده یا Consumer در نظر می گیریم. در نتیجه مقدار متغیر theme برابر با همان آبجکتی است که برای خصوصیت Value در Provider تعیین کرده ایم. مقدار darkMode را به متغیر darkMode منتسب می کنیم و در ادامه با توجه به این خصوصیت می توانیم تم Header را بخش های داخلی آن را تغییر دهیم.
نکته مهم دیگر متد changeTheme است که با کلیک بر روی دگمه تغییر تم در کامپوننت هدر اجرا می شود. این متد با استفاده از تابع setDarkMode که در Provider و در خصوصیت value مشخص شده است، در کامپوننت های داخلی در دسترس است. با فراخوانی این تابع darkMode state تغییر می کند در نتیجه مقدار جدید به تمامی کامپوننت های داخلی از جمله Header ارسال می شود و این کامپوننت مجدد با مقدار جدید رندر می شود و تم تغییر می کند.
فایل app.jsx را باز کنید و کد زیر را به آن اضافه کنید:
import Header from "./components/header";
import { useThemeContext } from "./context";
function App() {
const theme = useThemeContext();
const darkMode = theme.darkMode;
return (
<div
dir="rtl"
className={darkMode ? "bg-dark text-light" : "bg-light text-dark"}
>
<Header />
<div className="container p-4">
آینده، شناخت فراوان جامعه و متخصصان را می طلبد، تا با نرم افزارها شناخت
بیشتری را برای طراحان رایانه ای علی الخصوص طراحان خلاقی، و فرهنگ پیشرو
در زبان فارسی ایجاد کرد، در این صورت می توان امید داشت که تمام و دشواری
موجود در ارائه راهکارها، و شرایط سخت تایپ به پایان رسد و زمان مورد نیاز
شامل حروفچینی دستاوردهای اصلی، و جوابگوی سوالات پیوسته اهل دنیای موجود
طراحی اساسا مورد استفاده قرار گیرد.
</div>
</div>
);
}
export default App;
در app component هم به این دلیل که می خواهیم رنگ پس زمینه و رنگ متن را تغییر دهیم لازم است از useThemeContext استفاده کنیم و مقدار darkMode را استخراج و بر اساس آن css class های لازم را مشخص کنیم. فایل را سیو کنید و خروجی نهایی را مشاهده کنید: