react

مفهوم کانتکست(context) در react

با سلام خدمت همه عزیزان. در این مقاله با من همراه باشید تا  مفهوم context در ریکت با هم برررسی کنیم.

خب بریم سراغ ادامه مقاله…

لازم به ذکر است که این مقاله از سایت اصلی خود ریکت گردآوری شده است

  • “Context” یک روش فراهم می‌کند تا بتوانید داده‌ها را از طریق درخت مولفه‌ها منتقل کنید بدون اینکه به صورت دستی در هر سطح آنها را منتقل کنید.

    در یک برنامه React معمولی، داده‌ها به صورت بالا به پایین (از والد به فرزند) از طریق props منتقل می‌شوند، اما استفاده از این روش برای برخی از انواع props (مانند ترجیحات محلی، تم رابط کاربری) که توسط بسیاری از مولفه‌ها در یک برنامه نیاز است، ممکن است سنگین باشد. “Context” یک روش فراهم می‌کند تا بتوانید مقادیری مانند اینها را بین مولفه‌ها به اشتراک بگذارید بدون اینکه لازم باشد یک prop به صورت صریح از طریق هر سطح از درخت منتقل شود.

    حالا چه زمانی از کانتکست ها استفاده میکنیم؟
     
    زمان استفاده از Context: “Context” برای به اشتراک گذاری داده‌هایی که می‌توانند به عنوان “گلوبال” برای یک درخت از مولفه‌های React در نظر گرفته شوند، مانند کاربر فعلی احراز هویت شده، تم یا زبان ترجیحی. به عنوان مثال، در کد زیر، ما به صورت دستی یک prop به نام “theme” را از طریق مولفه دکمه منتقل می‌کنیم تا بتوانیم آن را برای استایل دهی به مولفه دکمه استفاده کنیم:
     
				
					class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // The Toolbar component must take an extra "theme" prop
  // and pass it to the ThemedButton. This can become painful
  // if every single button in the app needs to know the theme
  // because it would have to be passed through all components.
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}
				
			

با استفاده از “Context”، می‌توانیم از انتقال props از طریق عناصر واسطه‌ای خودداری کنیم:

				
					// Context lets us pass a value deep into the component tree
// without explicitly threading it through every component.
// Create a context for the current theme (with "light" as the default).
const ThemeContext = React.createContext('light');

class App extends React.Component {
  render() {
    // Use a Provider to pass the current theme to the tree below.
    // Any component can read it, no matter how deep it is.
    // In this example, we're passing "dark" as the current value.
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// A component in the middle doesn't have to
// pass the theme down explicitly anymore.
function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

class ThemedButton extends React.Component {
  // Assign a contextType to read the current theme context.
  // React will find the closest theme Provider above and use its value.
  // In this example, the current theme is "dark".
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
				
			
کانتکست

قبل از استفاده از Context: “Context” اصولا زمانی استفاده می‌شود که بخواهیم به داده‌ای توسط بسیاری از مولفه‌ها در سطوح مختلف دسترسی داشته باشیم. از آن به صورت محتاطانه استفاده کنید زیرا باعث سخت‌تر شدن استفاده مجدد از مولفه‌ها می‌شود.

اگر فقط می‌خواهید از انتقال برخی از props از طریق سطوح مختلف خودداری کنید، ترکیب مولفه‌ها اغلب راه حلی ساده‌تر از Context است.

به عنوان مثال، فرض کنید یک مولفه صفحه (Page) وجود دارد که یک prop به نام “user” و “avatarSize” را چندین سطح پایین‌تر منتقل می‌کند تا مولفه‌های ژرفا نهفته شده مانند Link و Avatar بتوانند آن را خوانده و استفاده کنند:

				
					<Page user={user} avatarSize={avatarSize} />
// ... which renders ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... which renders ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... which renders ...
<Link href={user.permalink}>
  <Avatar user={user} size={avatarSize} />
</Link>
				
			

ممکن است احساس کنید انتقال props کاربر و avatarSize از طریق چندین سطح اضافی است اگر در نهایت فقط مولفه Avatar به واقعیت نیاز دارد. همچنین اذیت‌کننده است که هر زمان که مولفه Avatar نیاز به props بیشتری از بالا دارد، شما باید آنها را به تمامی سطوح واسطی نیز اضافه کنید.

یک راه برای حل این مسئله بدون استفاده از Context این است که خود مولفه Avatar را انتقال دهید تا مولفه‌های واسطی نیازی به اطلاعات props کاربر یا avatarSize نداشته باشند:

				
					function Page(props) {
  const user = props.user;
  const userLink = (
    <Link href={user.permalink}>
      <Avatar user={user} size={props.avatarSize} />
    </Link>
  );
  return <PageLayout userLink={userLink} />;
}

// Now, we have:
<Page user={user} avatarSize={avatarSize} />
// ... which renders ...
<PageLayout userLink={...} />
// ... which renders ...
<NavigationBar userLink={...} />
// ... which renders ...
{props.userLink}
				
			

با این تغییر، تنها مولفه بالاترین Page نیاز به اطلاعات استفاده از کاربر و avatarSize در مولفه‌های Link و Avatar دارد.

این معکوس کردن کنترل می‌تواند در بسیاری از موارد کد شما را تمیزتر کند با کاهش تعداد propsی که باید از طریق برنامه‌ی شما منتقل شود و دادن بیشتری کنترل به مولفه‌های ریشه. این معکوس کردن، با این حال، در همه موارد بهترین انتخاب نیست؛ انتقال پیچیدگی بیشتر به سطح بالاتر در درخت، موجب می‌شود که مولفه‌های سطح بالاتر پیچیده‌تر شوند و مجبور به انعطاف بیشتری باشند تا آنچه شما می‌خواهید.

شما محدود به یک فرزند برای یک مولفه نیستید. شما می‌توانید چندین فرزند را منتقل کنید، یا حتی چندین “اسلات” جداگانه برای فرزندان داشته باشید، همانطور که در اینجا توضیح داده شده است:

				
					function Page(props) {
  const user = props.user;
  const content = <Feed user={user} />;
  const topBar = (
    <NavigationBar>
      <Link href={user.permalink}>
        <Avatar user={user} size={props.avatarSize} />
      </Link>
    </NavigationBar>
  );
  return (
    <PageLayout
      topBar={topBar}
      content={content}
    />
  );
}
				
			

این الگو برای بسیاری از موارد کافی است زمانی که نیاز به جداسازی یک فرزند از والدین فوری آن است. شما می‌توانید با استفاده از render props این الگو را به مراتب گسترش دهید اگر فرزند قبل از رندر شدن نیاز به ارتباط با والد دارد.

با این حال، گاهی اوقات نیاز است که همان داده توسط بسیاری از مولفه‌ها در درخت قابل دسترسی باشد، و در سطوح تو درو. Context به شما اجازه می‌دهد تا چنین داده‌ها و تغییراتی که در آنها اتفاق می‌افتد، را به تمامی مولفه‌های زیرانتقال دهید. مثال‌های معمولی که استفاده از Context ممکن است ساده‌تر از جایگزین‌ها باشد شامل مدیریت محل فعلی، تم یا حافظه نهان داده است.

خب حالا بریم سراغ استفاده از create context

				
					const MyContext = React.createContext(defaultValue);

				
			

React.createContext ساخت یک شیء Context را فراهم می‌کند. زمانی که React یک مولفه را رندر می‌کند که به این شیء Context مشترک می‌شود، ارزش فعلی Context را از نزدیکترین Provider مطابق بالای آن در درخت می‌خواند.

پارامتر defaultValue تنها زمانی استفاده می‌شود که یک مولفه مطابق Provider مطابق بالای خود در درخت نداشته باشد. ارزش پیش‌فرض می‌تواند برای تست کردن مولفه‌ها به صورت جداگانه بدون بسته‌بندی آنها مفید باشد. توجه: ارسال undefined به عنوان مقدار Provider منجر به استفاده از defaultValue توسط مولفه‌های مصرف‌کننده نمی‌شود.

استفاده از provider

				
					<MyContext.Provider value={/* some value */}>
				
			

هر شیء Context با یک مولفه Provider React همراه است که به مولفه‌های مصرف‌کننده اجازه می‌دهد تا به تغییرات Context مشترک مشترک شوند.

مولفه Provider یک prop به نام value را برای ارسال به مولفه‌های مصرف‌کننده که فرزندان این Provider هستند، می‌پذیرد. یک Provider می‌تواند به چندین مصرف‌کننده متصل شود. Providers می‌توانند به صورت تو در تو باشند تا مقادیر را در عمق‌ترین نقاط درخت بازنویسی کنند.

همه مصرف‌کنندگانی که فرزندان یک Provider هستند، هر زمان که prop مقدار Provider تغییر کند، مجدداً رندر خواهند شد. انتشار از Provider به مصرف‌کنندگان فرزند خود (شامل .contextType و useContext) نیازی به روش shouldComponentUpdate ندارد، بنابراین مصرف‌کننده بروزرسانی می‌شود حتی زمانی که یک مولفه پدر یک بروزرسانی را از دست می‌دهد.

تغییرات توسط مقایسه مقادیر جدید و قدیمی با استفاده از الگوریتم مشابه Object.is تعیین می‌شون

استفاده از contextType

				
					class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* perform a side-effect at mount using the value of MyContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* render something based on the value of MyContext */
  }
}
MyClass.contextType = MyContext;
				
			

ویژگی contextType روی یک کلاس می‌تواند یک شیء Context که توسط React.createContext() ایجاد شده است، اختصاص داده شود. استفاده از این ویژگی به شما امکان می‌دهد که مقدار فعلی نزدیکترین از نوع Context را با استفاده از this.context مصرف کنید. شما می‌توانید این را در هر یک از متدهای چرخه عمر از جمله تابع render مرجعی قرار دهید.

				
					class MyClass extends React.Component {
  static contextType = MyContext;
  render() {
    let value = this.context;
    /* render something based on the value */
  }
}

				
			

استفاده از Consumer

				
					<MyContext.Consumer>
  {value => /* render something based on the context value */}
</MyContext.Consumer>
				
			

یک مولفه React که به تغییرات Context مشترک مشترک می‌شود. استفاده از این مولفه به شما امکان می‌دهد تا به یک Context در یک مولفه تابع مشترک شوید.

نیازمند یک تابع به عنوان فرزند است. این تابع مقدار فعلی Context را دریافت می‌کند و یک گره React را برمی‌گرداند. آرگومان مقداری که به تابع منتقل می‌شود برابر با prop مقدار نزدیکترین Provider برای این Context در بالای درخت است. اگر هیچ Provider برای این Context در بالا وجود نداشته باشد، آرگومان مقدار برابر با defaultValue که به createContext() منتقل شده بود، خواهد بود.

استفاده از displayName

شیء Context یک خصوصیت رشته‌ای displayName را پذیرفته است. React DevTools از این رشته برای تعیین نمایش در مورد Context استفاده می‌کند.

به عنوان مثال، مولفه زیر به عنوان MyDisplayName در DevTools ظاهر می‌شود:

				
					const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> // "MyDisplayName.Provider" in DevTools
<MyContext.Consumer> // "MyDisplayName.Consumer" in DevTools
				
			

یک مثال پیچیده‌تر با مقادیر پویا برای تم:

theme-context.js

 
				
					export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(
  themes.dark // default value
);
				
			

themed-button.js

 
				
					import {ThemeContext} from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;
				
			

app.js

 
				
					import {ThemeContext, themes} from './theme-context';
import ThemedButton from './themed-button';

// An intermediate component that uses the ThemedButton
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };
  }

  render() {
    // The ThemedButton button inside the ThemeProvider
    // uses the theme from state while the one outside uses
    // the default dark theme
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(<App />);
				
			

Updating Context from a Nested Component اکثراً نیاز است که از یک مولفه که در یک نقطه عمیقاً در درخت مولفه‌ها قرار دارد، Context را به‌روز کنیم. در این صورت می‌توانید یک تابع را از طریق Context به پایین منتقل کنید تا به مصرف‌کنندگان این امکان داده شود تا Context را به‌روز کنند:

theme-context.js

 
				
					// Make sure the shape of the default value passed to
// createContext matches the shape that the consumers expect!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},
});
				
			

theme-toggler-button.js

 
				
					import {ThemeContext} from './theme-context';

function ThemeTogglerButton() {
  // The Theme Toggler Button receives not only the theme
  // but also a toggleTheme function from the context
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button
          onClick={toggleTheme}
          style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;
				
			

app.js

 
				
					import {ThemeContext, themes} from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme:
          state.theme === themes.dark
            ? themes.light
            : themes.dark,
      }));
    };

    // State also contains the updater function so it will
    // be passed down into the context provider
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,
    };
  }

  render() {
    // The entire state is passed to the provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <div>
      <ThemeTogglerButton />
    </div>
  );
}

const root = ReactDOM.createRoot(
  document.getElementById('root')
);
root.render(<App />);
				
			

Consuming Multiple Contexts برای حفظ سریع بودن بازنویسی Context، React نیاز دارد که هر مصرف‌کننده Context را به عنوان یک گره جداگانه در درخت قرار دهد.

				
					// Theme context, default to light theme
const ThemeContext = React.createContext('light');

// Signed-in user context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const {signedInUser, theme} = this.props;

    // App component that provides initial context values
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <div>
      <Sidebar />
      <Content />
    </div>
  );
}

// A component may consume multiple contexts
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}
				
			

اگر دو یا بیشتر مقدار Context اغلب به‌هم استفاده می‌شوند، ممکن است بخواهید یک مولفه render prop اختصاصی ایجاد کنید که هر دو را فراهم کند.

نکات مهم زیرا Context از هویت مرجع برای تعیین زمان بازنویسی استفاده می‌کند، برخی مسائلی وجود دارد که ممکن است باعث بازنویسی‌های ناخواسته در مصرف‌کنندگان شود زمانی که والدین یک ارائه‌دهنده مجدداً بازنویسی می‌شوند. به عنوان مثال، کد زیر همه مصرف‌کنندگان را هر بار که ارائه‌دهنده دوباره بازنویسی می‌شود، بازنویسی می‌کند زیرا همیشه یک شیء جدید برای مقدار ایجاد می‌شود:

				
					class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: 'something'}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}
				
			

برای دور زدن این موضوع، مقدار را به حالت والد ببرید:

				
					class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <MyContext.Provider value={this.state.value}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}
				
			

خب دوستان رسیدیم به پایان این مقاله. این مقاله از سایت اصلی ریکت  برای شما عزیزان گردآوری شده است

منبع : https://legacy.reactjs.org

محمد صفرپور

علاقمند به دنیای برنامه نویسی مخصوصا فرانت اند😍

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا