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

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

وقتی خواندن را به پایان برسانید ، می توانید خودتان تشخیص دهید چه زمانی ایجاد اجزای React قابل استفاده مجدد مناسب است و چگونه انجام این کار

این مقاله دانش اساسی در مورد قلاب های React و React را فرض می کند. اگر می خواهید در مورد این موضوعات صحبت کنید ، توصیه می کنم راهنمای “شروع به کار با React” و “Introduction to React Hooks” را مطالعه کنید.

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

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

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

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

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


import './common.css';

function Login() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

import './common.css';

function Signup() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

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


import Footer from "./Footer.js";

function Login() {
  return (
    <Wrapper main={{...}} footer={<Footer />} />
  );
} 

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

استفاده مکرر از شنوندگان رویداد

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


import { useEffect } from 'react';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);

  return (...);
}

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


function Button() {
  return (
    <button type="button" onClick={() => { alert('Hi!')}}>
      Click me!
    </button>
  );
};

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

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

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

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


import { useEffect } from 'react';

export default function useKeydown() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);
};

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


import useKeydown from './useKeydown.js';

function App() {
  useKeydown();
  return (...);
};

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


import { useEffect } from 'react';

export default function useEventListener({ event, handler} ) {
  useEffect(() => {
    document.addEventListener(event, props.handler);
    return () => {
      document.removeEventListener(event, props.handler);
    }
  }, []);
};

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


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>
  );
};

ایجاد سه م Reلفه واکنش مجدد قابل استفاده مجدد

اکنون که برخی از علائم رایج را مشاهده کرده ایم چه زمانی برای ایجاد یک م componentلفه جدید که می توانید در سراسر برنامه واکنش خود به اشتراک بگذارید ، بیایید این دانش را در عمل به کار گیریم و سه نسخه نمایشی عملی بسازیم.

1. جزء طرح بندی

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

جزء عملکردی React زیر را در نظر بگیرید:


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];

این یک صفحه وب معمولی است که دارای یک <header>، آ <main> و الف <footer>به اگر 30 صفحه وب دیگر مانند این وجود داشته باشد ، به راحتی از نوشتن مکرر برچسب های HTML و اعمال یک سبک بارها و بارها خسته می شوید.

در عوض ، می توانید یک جزء طرح بندی ایجاد کنید که دریافت می کند <header>، <main> و <footer> به عنوان لوازم جانبی ، مانند کد زیر:


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>به بنابراین ، می توانید از این طرح بندی یکسان برای صفحات صرف نظر از اینکه دارای سرصفحه یا پاورقی باشند استفاده کنید.

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


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];

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

بسیاری از توسعه دهندگان تمایل به استفاده دارند position: fixed یا position: absolute وقتی می خواهند یک سرصفحه به بالای نمای نما یا یک پاورقی به پایین بچسبانند. با این حال ، در مورد طرح بندی ، باید سعی کنید از این امر جلوگیری کنید.

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

در اینجا مثالی آورده شده است:


.Container {
  
  display: flex;
  flex-direction: column;

  
  width: 100%;
  height: 100%;

  
  overflow: hidden;
  position: fixed;
}

.Main {
  
  width: 100%;
  height: 100%;

  
  overflow-y: scroll;
}

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


.FeedHeader {
  
  height: 70px;

  
  background-color: teal;
  color: beige;
}

.FeedFooter {
  
  height: 70px;

  
  background-color: beige;
  color: teal;
}

.ItemList {
  
  display: flex;
  flex-direction: column;
}

.Item {
  
  height: 300px;

  
  color: teal;
}

.FeedHeader,
.FeedFooter,
.Item {
  
  display: flex;
  justify-content: center;
  align-items: center;

  
  border: 1px solid teal;

  
  font-size: 35px;
}

سربرگ چسبنده و نمایشی پاورقی

و در اینجا کد در عمل است.

این چیزی است که در صفحه های رومیزی به نظر می رسد.

Desktop_Layout

این چیزی است که در صفحه نمایش تلفن همراه به نظر می رسد.

Mobile_Layout

این طرح بندی در دستگاه های iOS نیز همانطور که در نظر گرفته شده کار می کند! در صورتی که نمی دانید ، iOS به دلیل ایجاد مشکلات غیرمنتظره مرتبط با موقعیت در توسعه برنامه های وب مشهور است.

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

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

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


import { useEffect } from "react";

export default function useScrollSaver(scrollableDiv, pageUrl) {
  
  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]);

  
  useEffect(() => {
    if (
      scrollableDiv.current &&
      sessionStorage.getItem(`${pageUrl}-scrollPosition`)
    ) {
      const prevScrollPos = Number(
        sessionStorage.getItem(`${pageUrl}-scrollPosition`)
      );
      scrollableDiv.current.scrollTop = prevScrollPos;
    }
  }, [scrollableDiv, pageUrl]);
}

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

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

اول از همه ، شما باید یک شنونده رویداد “scroll” را به ظرف قابل پیمایش خود متصل کنید:

const scrollableBody = scrollableDiv.current;
scrollableBody.addEventListener("scroll", handleScroll);
return function cleanup() {
  scrollableBody.removeEventListener("scroll", handleScroll);
};

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

const handleScroll = () => {
  sessionStorage.setItem(
    `${pageUrl}-scrollPosition`,
    scrolledDiv.current.scrollTop.toString()
  );
};

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

هنگامی که یک کاربر به صفحه وب برمی گردد ، کاربر باید به موقعیت پیمایش قبلی خود هدایت شود – در صورت وجود. این اطلاعات موقعیت در حال حاضر در ذخیره می شود sessionStorage، و شما باید آن را بیرون آورده و از آن استفاده کنید. شما می توانید استفاده کنید getItem(id: string) برای به دست آوردن اطلاعات از ذخیره سازی سپس ، شما فقط باید تنظیم کنید scroll-top ظرف قابل پیمایش به این مقدار بدست آمده:

const prevScrollPos = Number(
  sessionStorage.getItem(`${pageUrl}scrollPosition`)
);
scrollableDiv.current.scrollTop = prevScrollPos;

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

اکنون که ایجاد قلاب سفارشی خود را به پایان رسانده اید ، می توانید از قلاب در هر صفحه وب مورد نظر خود استفاده کنید تا زمانی که دو مورد مورد نیاز را به قلاب منتقل کنید: scrollableDiv و pageUrlبه برگردیم به 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 اجرا می شود. سعی کنید صفحه را پیمایش کنید ، سپس از پیکان در پایین سمت چپ و گوشه برای بارگیری مجدد برنامه استفاده کنید.

شما خود را در جایی که ترک کرده اید قرار خواهید گرفت!

3. Query/Mutation (مخصوص GraphQL)

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

برای اجرای پرس و جو GraphQL مثال زیر را در نظر بگیرید 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 هر بار که نیاز دارید پرس و جو را اجرا کنید getPosts()، می توانید قلاب React زیر را ایجاد کنید:


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() به صورت زیر قلاب کنید:


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 استفاده می کنید. با استفاده از این تکنیک های تغییر شکل ، تیم توسعه ما در خاک رس توانست پایگاه کد ما را به اندازه قابل کنترل کاهش دهد. امیدوارم شما هم بتونید!