ابتدا شیء user، در بالاترین سطح، دریافت شده و به صفحهای خاص از طریق ویژگیهای props ارسال میشود:
<Page user={user} />
<PageLayout user={user} />
<NavigationBar user={user} />
ایجاد شیء Context در برنامههای React
React Context، راه حلی است جهت به اشتراک گذاری دادهها، در بین انواع و اقسام کامپوننتهای یک برنامه، بدون اینکه نیازی باشد این اطلاعات را توسط props، از یک سطح، به سطحی دیگر، به صورت دستی انتقال داد. برای ایجاد یک نمونهی از آن، ابتدا پوشهی جدید src\contexts را افزوده و سپس فایل src\contexts\userContext.js را درون آن، با محتوای زیر ایجاد میکنیم:
import React from "react"; export const UserContext = React.createContext({ user: {} }); export const UserProvider = UserContext.Provider; export const UserConsumer = UserContext.Consumer;
تامین یک شیء Context در برنامه، در یک کامپوننت کلاسی و یا تابعی
تا اینجا یک شیء Context را به همراه اجزای export شدهی Provider و Consumer آن ایجاد کردیم. اکنون نوبت به پیاده سازی قسمت Provider آن است:
import "../../App.css"; import React, { Component } from "react"; import { UserProvider } from "../../contexts/userContext"; import Main from "./Main"; class App extends Component { state = { user: { name: "User 1" } }; componentDidMount() { // get user from the server or local storage and then set the currently logged in user to the this.state } render() { return ( <> <h1>App Class</h1> <UserProvider value={this.state.user}> <Main /> </UserProvider> </> ); } } export default App;
در ادامه قصد داریم اطلاعات این شیء user موجود در state را با تمام کامپوننتهایی که در درخت رندر کامپوننت جاری قرار میگیرند و با کامپوننت Main شروع میشوند، به اشتراک بگذاریم. این به اشتراک گذاری با import شیء UserProvider از ماژول contexts/userContext به نحوی که مشاهده میکنید، انجام میشود. شیء UserProvider، کار محصور سازی کامپوننت Main را انجام میدهد. سپس این Provider میتواند مقداری را توسط ویژگی value خود دریافت کند که برای مثال در اینجا شیء user است. اکنون این value تا n سطح بعدی که از کامپوننت Main مشتق میشوند نیز در دسترس خواهد بود.
یک نکته: متد React.createContext به همراه یک آرگومان defaultValue اختیاری است که در اختیار Consumerهای آن قرار داده میشود؛ اگر Provider متناظر با آن، در درخت کامپوننتهای برنامه، یافت نشود. یعنی تعریف Provider الزامی نیست. اگر نیاز است مقدار ثابتی را بین چندین کامپوننت به اشتراک بگذارید، فقط کافی است آنها را توسط React.createContext مقدار دهی اولیه کرده و ... استفاده کنید:
export const DefaultRouteContext = React.createContext({ path: '/welcome' });
خواندن شیء Context در کامپوننتی دیگر
اکنون که یک تامین کنندهی Context را ایجاد کردیم، برای خواندن اطلاعات آن در درخت کامپوننتهای محصور شدهی توسط UserProvider، میتوان به صورت زیر عمل کرد:
import React from "react"; import { UserConsumer } from "../../contexts/userContext"; export default function Main(props) { return ( <> <UserConsumer> {value => <div>User name: {value.name}.</div>} </UserConsumer> </> ); }
خروجی برنامه پس از این تغییرات به صورت زیر است:
ساده سازی دسترسی به UserConsumer توسط useContext Hook
نحوهی تعریف یک Provider و محصور سازی فرزندانی که باید از آن ارثبری کنند، در بین کامپوننتهای کلاسی و تابعی، یکی است. اما در کامپوننتهای تابعی حداقل میتوان نحوهی دسترسی به UserConsumer را به نحو زیر توسط useContext Hook ساده کرد:
import React, { useContext } from "react"; import { UserContext } from "../../contexts/userContext"; export default function Main() { const value = useContext(UserContext); return ( <> <div>User name: {value.name}.</div> </> ); }
مزیت دیگر این روش، ساده سازی کار با چندین شیء Context است. برای مثال اگر دو شیء Context را تعریف کرده باشید، خواندن دو مقدار از آنها، پیشتر چنین شکل تو در تویی را توسط دو Consumer پیدا میکرد:
function HeaderBar() { return ( <CurrentUser.Consumer> {user => <Notifications.Consumer> {notifications => <header> Welcome back, {user.name}! You have {notifications.length} notifications. </header> } } </CurrentUser.Consumer> ); }
function HeaderBar() { const user = useContext(CurrentUser); const notifications = useContext(Notifications); return ( <header> Welcome back, {user.name}! You have {notifications.length} notifications. </header> ); }
ارسال اطلاعات به کامپوننت Context Provider، از طریق کامپوننتهای فرزند
تا اینجا با استفاده از React Context، اطلاعات یک Provider را با فرزندان آن به اشتراک گذاشتیم؛ عکس این عمل نیز میسر است. برای اینکار، همانند تمام کامپوننتهای دیگری که برای ارسال اطلاعات به فراخوان خود از طریق رخدادها عمل میکنند، میتوان یک متد رویدادگردان را در کامپوننت والد، به استفاده کنندهی از Context ارسال کرد:
import "../../App.css"; import React, { Component } from "react"; import { UserProvider } from "../../contexts/userContext"; import Main from "./Main2"; class App extends Component { state = { user: { name: "User 1" } }; componentDidMount() { // get user from the server or local storage and then set the currently logged in user to the this.state } logout = () => { console.log("logout"); this.setState({ user: {} }); }; render() { const contextValue = { user: this.state.user, logoutUser: this.logout }; return ( <> <h1>App Class</h1> <UserProvider value={contextValue}> <Main /> </UserProvider> </> ); } } export default App;
اکنون تمام استفاده کنندههای از این UserProvider میتوانند با فراخوانی متد منتسب به logout، سبب پاک شدن اطلاعات کاربر موجود در state کامپوننت App، به روز رسانی state و در نتیجهی آن، رندر مجدد کامپوننت و ارائهی یک UserProvider جدید، با اطلاعاتی جدید به فرزندان آن شوند:
import React, { useContext } from "react"; import { UserContext } from "../../contexts/userContext"; export default function Main() { const { user, logoutUser } = useContext(UserContext); return ( <> <div>User name: {user.name}.</div> <button type="button" className="btn btn-primary" onClick={logoutUser}> Logout user </button> </> ); }
روش انجام اینکار بدون استفاده از useContext را نیز در ادامه مشاهده میکنید که در ابتدا نیاز به تعریف تابعی را دارد که همان خواص استخراجی را دریافت میکند. سپس باید بر اساس آنها، المانهای مدنظر نمایش نام کاربر و دکمهی خروج او را بازگشت داد:
import React from "react"; import { UserConsumer } from "../../contexts/userContext"; export default function Main(props) { return ( <> <UserConsumer> {({ user, logoutUser }) => ( <> <div>User name: {user.name}.</div> <button type="button" className="btn btn-primary" onClick={logoutUser} > Logout user </button> </> )} </UserConsumer> </> ); }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: sample-30-part-04.zip