استفاده از مجدد از کامپوننت های react

راهنمای عملی ایجاد کامپوننت های React با قابلیت استفاده مجدد

راهنمای عملی ایجاد کامپوننت های React با قابلیت استفاده مجدد

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

در این آموزش، شما با سه شاخص متداول آشنا می شوید که بوسیله آنها میتوانید یک کامپوننت React قابل استفاده مجدد را ایجاد کنید. سپس با ساختن یک طرح قابل استفاده مجدد و دو هوک در React، برخی از موارد عملی را بررسی می کنیم.

هنگامی که خواندن را به پایان رساندید، میتوانید به تنهایی تعیین کنید که چه زمانی مناسب است از کامپوننت های React مجدداً استفاده کنید و چگونه این کار را انجام دهید.

سه شاخص برتر یک مولفه قابل قابل استفاده مجدد React

ابتدا اجازه دهید برخی از نشانه ها را در مورد زمانی که ممکن است بخواهید این کار را انجام دهید، بررسی کنیم.

ایجاد تکرار بسته بندی با سبک CSS

نشانه مورد علاقه من برای دانستن زمان ایجاد یک مولفه قابل استفاده مجدد، استفاده مستمر از همان سبک CSS است. اکنون، ممکن است فکر کنید، چرا من به سادگی یک نام کلاس را به عناصری که دارای سبک CSS یکسانی هستند اختصاص نمی دهم؟. کاملاً حق با شماست. ایده خوبی نیست؛ هر بار که برخی از عناصر در مولفه های مختلف دارای سبک مشابهی باشند، کولفه قابل استفاده مجدد ایجاد شود. در واقع، ممکن است باعث پیچیدگی غیرضروری شود. بنابراین شما باید یک سوال دیگر نیز از خود بپرسید: آیا این عناصر معمولاً دارای Wrapprer هستند؟

به عنوان مثال، صفحات ورود و ثبت نام را در نظر بگیرید:

// Login.js

import ‘./common.css’;

 

function Login() {

  return (

    <div className=’wrapper‘>

      <main>

        {}

      </main>

      <footer className=’footer‘>

        {}

      </footer>

    </div>

  );

}

// SignUp.js

import ‘./common.css’;

 

function Signup() {

  return (

    <div className=’wrapper‘>

      <main>

        {}

      </main>

      <footer className=’footer‘>

        {}

      </footer>

    </div>

  );

}

سبک های مشابهی روی کانتینر(عنصر<div>) و فوتر هر مولفه‌ای اعمال می شود. بنابراین در این مورد، می توانید دو مولفه قابل استفاده مجدد – <Wrapper /> و <Footer /> – ایجاد کرده و از آنها برای حمایت Children استفاده کنید. به عنوان مثال، مولفه ورود را می توان به صورت زیر تغییر شکل داد:

// Login.js

import Footer from “./Footer.js”;

 

function Login() {

  return (

    <Wrapper main={{}} footer={<Footer />} />

  );

} 

در نتیجه، دیگر نیازی به وارد کردن common.css در چندین صفحه یا ایجاد عناصر <div> یکسان برای بسته بندی همه چیز ندارید.

استفاده مکرر از شنوندگان رویداد(Repetitive use of event listeners):

برای  اتصال یک شنونده رویداد به یک عنصر، می توانید آن را در useEffect ()مانند زیر مدیریت کنید:

// App.js

import { useEffect } from ‘react’;

 

function App() {

  const handleKeydown = () => {

    alert(‘key is pressed.’);

  }

 

  useEffect(() => {

    document.addEventListener(‘keydown’, handleKeydown);

    return () => {

      document.removeEventListener(‘keydown’, handleKeydown);

    }

  }, []);

 

  return ();

}

یا می توانید این کار را مستیقیماً در داخل JSX خود انجام دهید، همانطور که در دکمه کامپوننت زیر نشان داده شده است:

// Button.js

function Button() {

  return (

    <button type=”button onClick={() => { alert(‘Hi!’)}}>

      Click me!

    </button>

  );

};

هنگامی که می خواهید یک شنونده رویداد را به سند یا پنجره‌ای اضافه کنید، باید از روش اول استفاده کرد. با این حال، همانطور که ممکن است قبلا متوجه شده باشید، اولین روش نیاز به کد بیشتری با استفاده از addEventListener()، useEffect() و removeEventListener() دارد. بنابراین در چنین مواردی، ایجاد یک هوک سفارشی به کامپوننت های شما اجازه می دهد تا مختصرتر باشند.

چهار سناریوی احتمالی برای استفاده از شنوندگان رویداد وجود دارد:

همان شنونده رویداد، همان گرداننده رویداد
– شنونده رویداد یکسان، کنترل کننده رویداد متفاوت
– شنونده رویداد متفاوت، کنترل کننده رویداد یکسان
– شنوینده رویداد متفاوت، مدیریت رویداد متفاوت

در سناریوی اول، می توانی یک هوک ایجاد کنید که هم شنونده رویداد و هم مدیریت رویداد در آن تعریف شده باشد. هوک زیر را درنظر بگیرید:

// useEventListener.js

import { useEffect } from ‘react’;

 

export default function useKeydown() {

  const handleKeydown = () => {

    alert(‘key is pressed.’);

  }

 

  useEffect(() => {

    document.addEventListener(‘keydown’, handleKeydown);

    return () => {

      document.removeEventListener(‘keydown’, handleKeydown);

    }

  }, []);

};

سپس می توانید از این هوک در هر کامپوننت به شرح زیر استفاده کنید:

// App.js

import useKeydown from ‘./useKeydown.js’;

 

function App() {

  useKeydown();

  return ();

};

برای سه سناریوی دیگر، توصیه میشود که قلابی ایجاد کنید که یک رویداد و عملکرد مدیریت رویداد را به عنوان لوازم جانبی دریافت کند. به عنوان مثال، من keydown و handleKeydown را به عنوان پشتیبان به هوک سفارشی خود منتقل می کنم. هوک زیر را در نظر بگیرید:

// App.js

import useEventListener from ‘./useEventListener.js’;

 

function App() {

  const handleKeydown = () => {

    alert(‘key is pressed.’);

  }

  useEventListener(‘keydown’, handleKeydown);

  return ();

};

استفاده مکرر از همان اسکریپت GraphQL

در مورد استفاده مجدد از کد GraphQL نیازی نیست به دنبال علائم باشید. برای برنامه های پیچیده، اسکریپت های GraphQL برای یک پرس و جو به راحتی 30 تا 50 خط کد را اشغال می کنند. زیرا ویژگی های زیادی برای درخواست وجود دارد. اگر از یک یا دو بار از یک اسکریپت GraphQL استفاده می کنید، فکر میکنم که به هوک سفارشی خود نیاز دارد.

به مثال زیر توجه کنید:

import { gql, useQuery } from “@apollo/react-hooks”;

 

const GET_POSTS = gql`

  query getPosts {

    getPosts {

    user {

      id

      name

      

      }

      emojis {

         id

         

      }

      

  }

`;

 

const { data, loading, error } = useQuery(GET_POSTS, {

  fetchPolicy: “network-only”

});

به جای تکرار این کد در هر صفحه‌ای که پست ها را از بک-اند درخواست می کند، باید یک هوک React برای این API خاص ایجاد کنید:

import { gql, useQuery } from “@apollo/react-hooks”;

 

function useGetPosts() {

  const GET_POSTS = gql`{}`;

  const { data, loading, error } = useQuery(GET_POSTS, {

    fetchPolicy: “network-only”

  });

  return [data];

}

 

const Test = () => {

  const [data] = useGetPosts();

  return (

    <div>{data?.map(post => <h1>{post.text}</h1>)}</div>

  );

};

ایجاد سه مولفه  React قابل استفاده مجدد

اکنون که ما نشانه های متداول در مورد زمان ایجاد یک کامپوننت جدید که می توانید در سراسر برنامه React خود به اشتراک بگذارید را مشاهده کرده‌ایم، بگذارید این دانش را در عمل به کار گیریم و سه نسخه نمایشی عملی بسازیم

1. کامپوننت طرح بندی

React معمولاً برای ایجاد برنامه های پیچیده وب استفاده می شود. این بدان معناست که تعداد زیادی صفحه باید در React توسعه داده شود و من شک دارم که هر صفحه از یک برنامه دارای طرح بندی متفاوتی باشد. به عنوان مثال، یک برنامه وب متشکل از 30 صفحه معمولاً از کمتر از پنج طرح بندی مختلف استفاده می کند. بنابراین، ایجاد یک طرح بندی انعطاف پذیر و قبل استفاده مجدد که بتواند در بسیاری از صفحات مختلف استفاده شود ضروری است. این مسئله باعث می شود که خطوط زیادی از کد و در نتیجه زمان فوق العاده‌ای برای شما ذخیره شود.

کامپوننت عملکردی React زیر را در نظر بگیرید:

// Feed.js

import React from “react”;

import style from “./Feed.module.css”;

 

export default function Feed() {

  return (

    <div className={style.FeedContainer}>

      <header className={style.FeedHeader}>Header</header>

      <main className={style.FeedMain}>

        {

          <div className={style.ItemList}>

            {itemData.map((item, idx) => (

              <div key={idx} className={style.Item}>

                {item}

              </div>

            ))}

          </div>

        }

      </main>

      <footer className={style.FeedFooter}>Footer</footer>

    </div>

  );

}

 

const itemData = [1, 2, 3, 4, 5];

این یک صفحه معمولی وب است که دارای <main>، <header> و <footer> را به عنوان props دریافت می کند. مانند کد زیر:

// Layout.js

import React from “react”;

import style from “./Layout.module.css”;

import PropTypes from “prop-types”;

 

export default function Layout({ header, main, footer }) {

  return (

    <div className={style.Container}>

      <header className={style.Header}>{header}</header>

      <main className={style.Main}>{main}</main>

      <footer className={style.Footer}>{footer}</footer>

    </div>

  );

}

 

Layout.propTypes = {

  main: PropTypes.element.isRequired,

  header: PropTypes.element,

  footer: PropTypes.element

};

این کامپوننت به <header> و <footer> نیاز ندارد. بنابراین، می توانید از طرح بندی یکسان برای صفحات استفاده کنید، صرف نظر از اینکه دارای header یا footer باشند.

با استفاده از این کامپوننت طرح بندی، می توانید صفحه feed خود را به یک بلوک کد پیچیده تر تبدیل کنید:

// Feed.js

import React from “react”;

import Layout from “./Layout”;

import style from “./Feed.module.css”;

 

export default function Feed() {

  return (

    <Layout

      header={<div className={style.FeedHeader}>Header</div>}

      main={

        <div className={style.ItemList}>

          {itemData.map((item, idx) => (

            <div key={idx} className={style.Item}>

              {item}

            </div>

          ))}

        </div>

      }

      footer={<div className={style.FeedFooter}>Footer</div>}

    />

  );

}

 

const itemData = [1, 2, 3, 4, 5];

 

نکته‌ حرفه‌ای برای ایجاد طرح بندی با عناصر چسبیده

بسیاری از توسعه دهندگان تمایل دارند یک header را به بالای صفحه نمایش یا یک footer را به پایین بچسبانند، از موقعیت ثابت یا موقعیت مطلق استفاده کنند. با این حال، در مورد طرح بندی، سعی کنید از این امر جلوگیری کنید.

از آنجا که عناصر یک طرح، عناصر اصلی لوازم جانبی منتقل شده خواهند بود، شما می خواهید سبک عناصر طرح خود را تا حد ممکن ساده نگه دارید. به طوریکه <header>، <main> یا <footer> به صورت دلخواه طرح بندی شوند. بنابراین، من توصیه میکنم از موقعیت Fixed and display: flex به بیرونی ترین عنصر طرح خود استفاده کنید و تنظیم overflow-y: به عنصر <main> بروید.

مثال زیر را مشاهده کنید:

/* Layout.module.css */

.Container {

  /* Flexbox */

  display: flex;

  flex-direction: column;

 

  /* Width & Height */

  width: 100%;

  height: 100%;

 

  /* Misc */

  overflow: hidden;

  position: fixed;

}

 

.Main {

  /* Width & Height */

  width: 100%;

  height: 100%;

 

  /* Misc */

  overflow-y: scroll;

}

 اکنون اجازه دهید برخی از سبک ها را در صفحه feed خود اعمال کنیم و ببینیم چه چیزی ساخته‌اید:

/* Feed.module.css */

.FeedHeader {

  /* Width & Height */

  height: 70px;

 

  /* Color & Border */

  background-color: teal;

  color: beige;

}

 

.FeedFooter {

  /* Width & Height */

  height: 70px;

 

  /* Color & Border */

  background-color: beige;

  color: teal;

}

 

.ItemList {

  /* Flexbox */

  display: flex;

  flex-direction: column;

}

 

.Item {

  /* Width & Height */

  height: 300px;

 

  /* Misc */

  color: teal;

}

 

.FeedHeader,

.FeedFooter,

.Item {

  /* Flexbox */

  display: flex;

  justify-content: center;

  align-items: center;

 

  /* Color & Border */

  border: 1px solid teal;

 

  /* Misc */

  font-size: 35px;

}

2. شنونده رویداد

اغلب، یک شنونده یک رویداد بیش از یک بار در سراسر برنامه وب استفاده می شود. در چین مواردی، ایجاد هوک سفارشی React ایده خوبی است. بیایید یاد بگیریم که چگونه این کار را با ایجاد یک هوک useScrollSaver انجام دهیم، که موقعت پیمایش دستگاه کاربر را در یک صفحه ذخیره می کند، به طوری که کاربر نیازی به پیمایش دوباره ار بالا ندارد. این هوک برای صفحه وب نیز مفید خواهد بود که در آن تعداد زیادی عناصر مانند پست و نظرات ذکر شده است. صفحات feed فیسبوک، اینستاگرام و توییتر را بدون محافظ اسکرول تصور کنید.

بیایید کد زیر را بررسی کنیم:

// useScrollSaver.js

import { useEffect } from “react”;

 

export default function useScrollSaver(scrollableDiv, pageUrl) {

  /* Save the scroll position */

  const handleScroll = () => {

    sessionStorage.setItem(

      `${pageUrl}-scrollPosition`,

      scrollableDiv.current.scrollTop.toString()

    );

  };

  useEffect(() => {

    if (scrollableDiv.current) {

      const scrollableBody = scrollableDiv.current;

      scrollableBody.addEventListener(“scroll”, handleScroll);

      return function cleanup() {

        scrollableBody.removeEventListener(“scroll”, handleScroll);

      };

    }

  }, [scrollableDiv, pageUrl]);

 

  /* Restore the saved scroll position */

  useEffect(() => {

    if (

      scrollableDiv.current &&

      sessionStorage.getItem(`${pageUrl}-scrollPosition`)

    ) {

      const prevScrollPos = Number(

        sessionStorage.getItem(`${pageUrl}-scrollPosition`)

      );

      scrollableDiv.current.scrollTop = prevScrollPos;

    }

  }, [scrollableDiv, pageUrl]);

}

می بینید که هوک useScrollSaver باید دو مورد را دریافت کند: Scrllablediv، که باید مانند ظرف <main> در طرح بندی شما در بالا یک کانتینر قابل پیمایش باشد و pageUrl، که به عنوان شناسنامه صفحه مورد استفاده قرار می گیرد تا بتوانید موقعیت های پیمایش چندین صفحه را ذخیره کنید.

مرحله1: موقعیت اسکرول را ذخیره کنید

اول از همه، شما باید یک شنونده رویداد «scroll» را به کانتینر قابل پیمایش خود متصل کنید:

const scrollableBody = scrollableDiv.current;

scrollableBody.addEventListener(“scroll”, handleScroll);

return function cleanup() {

  scrollableBody.removeEventListener(“scroll”, handleScroll);

};

در حال حاضر، هر بار که scrollable div توسط کاربر پیمایش می شود، تابعی بهنام handleScroll اجرا می شود. در این تابع، شما باید از localStorage یا sessionStorage برای ذخیره موقعیت اسکرول استفاده کنید. تفاوت این دو در این است که داده ها در localStorage منقضی نمی شوند، در حالی که داده ها در sessionStorage با پایان جلسه، صفحه پاک می شوند. شما می توانید از setltem (id: string, value: string) برای ذخیره داده ها در هر دو ذخیره سازی استفاده کنید: 

const handleScroll = () => {

  sessionStorage.setItem(

    `${pageUrl}-scrollPosition`,

    scrolledDiv.current.scrollTop.toString()

  );

};

مرحله2: موقعیت اسکرول را بازیابی کنید.

هنگامی که یک کاربر به صفحه وب بر می گردد، کاربر باید به موقعیت پیمایش قبلی خود هدایت شود. این اطلاعات موقعیت در حال حاضر در sessionStorage ذخیره می شود و شما باید آن را بیرون آورده و از آن استفاده کنید. سپس، شما فقط باید اسکرول بالای کانتینر قابل پیمایش را روی این مقدار بدست آمده تنظیم کنید:

const prevScrollPos = Number(

  sessionStorage.getItem(`${pageUrl}scrollPosition`)

);

scrollableDiv.current.scrollTop = prevScrollPos;

مرحله 3: از هوک  useScollSaver در هر صفحه وب استفاده کنید

اکنون که ایجاد هوک سفارشی خود را به پایان رسانده‌اید، می توانید از هوک در هر صفحه وب مورد نظر خود استفاده کنید به شرط اینکه موارد مورد نیاز را به هوک scrollableDiv و pageUrl منتقل کنید. بیایید به Layout.js برگردیم و از هوک خود در آنجا استفاده کنیم. این به هر صفحه وب که از این طرح استفاده می کند اجازه می دهد از محافظ پیمایش شما لذت ببرد:

// Layout.js

import React, { useRef } from “react”;

import style from “./Layout.module.css”;

import PropTypes from “prop-types”;

import useScrollSaver from “./useScrollSaver”;

 

export default function Layout({ header, main, footer }) {

  const scrollableDiv = useRef(null);

  useScrollSaver(scrollableDiv, window.location.pathname);

  return (

    <div className={style.Container}>

      <header className={style.Header}>{header}</header>

      <main ref={scrollableDiv} className={style.Main}>

        {main}

      </main>

      <footer className={style.Footer}>{footer}</footer>

    </div>

  );

}

نسخه دمو Scrollsaver:

و در اینجا کد در Sandbox اجرا می شود. سعی کنید صفحه را پیمایش کنید، سپس از پیکان در پایین سمت چپ و گوشه برای بارگیری مجدد برنامه استفاده کنید.

خود را در جایی که کار را متوقف کرده‌اید؛ پیدا خواهید کرد.

Query/Mutation (مخصوص GraphQL)

اگر دوست دارید مانند من از GraphQL با React استفاده کنید، با ایجاد یک هوک React برای جستارها یا جهش های GraphQL، پایگاه کد را حتی بیشتر کاهش دهید.

مثال زیر را برای اجرای ()GraphQL query getPosts در نظر بگیرید:

import { gql, useQuery } from “@apollo/react-hooks”;

 

const GET_POSTS = gql`

  query getPosts {

    getPosts {

    user {

      id

      name

      

      }

      emojis {

         id

         

      }

      

  }

`;

 

const { data, loading, error } = useQuery(GET_POSTS, {

  fetchPolicy: “network-only”

});

اگر ویژگی های بیشتری برای درخواست از سمت بک-اند وجود داشته باشد، اسکریپت GraphQL شما فضای بیشتری را اشغال می کند. بنابراین، به جای تکرار اسکریپت GraphQL و useQuery هر بار که نیاز به اجرای query getPosts دارید، می توانید هوک React زیر را ایجاد کنید:

// useGetPosts.js

import { gql, useQuery } from “@apollo/react-hooks”;

 

export default function useGetPosts() {

  const GET_POSTS = gql`

  query getPosts {

    getPosts {

    user {

      id

      name

      

      }

      emojis {

         id

         

      }

      

  }

  `;

 

  const { data, loading, error } = useQuery(GET_POSTS, {

    fetchPolicy: “network-only”

  });

 

  return [data, loading, error];

}

سپس، می توانید از هوک() useGetPosts خود به شرح زیر استفاده کنید:

// Feed.js

import React from “react”;

import Layout from “./Layout”;

import style from “./Feed.module.css”;

import useGetPosts from “./useGetPosts.js”;

 

export default function Feed() {

  const [data, loading, error] = useGetPosts();

  return (

    <Layout

      header={<div className={style.FeedHeader}>Header</div>}

      main={

        <div className={style.ItemList}>

          {data?.getPosts.map((item, idx) => (

            <div key={idx} className={style.Item}>

              {item}

            </div>

          ))}

        </div>

      }

      footer={<div className={style.FeedFooter}>Footer</div>}

    />

  );

}

نتیجه

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

بستن