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

چرا باید از کامپوننت Reusable در پروژه خود استفاده کنیم؟

همانطور که وب‌سایت ما رشد می‌کند و بزرگ‌تر می‌شود، می‌توانیم به راحتی ویژگی‌های جدید را با کامپوننت‌‌هایی که داریم ترکیب نموده و به پروژه اضافه کنیم. این کار باعث می‌شود تا کد ما مقیاس‌پذیرتر بوده و سازگارتر باشد.

همچنین می‌توانیم از کدهایی که قابلیت استفاده مجدد دارند، بدون اینکه نیاز باشدآن‌ها را دوباره از ابتدا بنویسیم در پروژه‌های آینده مورد استفاده قرار دهیم.

نحوه ساخت کامپوننت Reusable در React

دو نکته مهم وجود دارد که هنگام ساخت کامپوننت reusable در React باید به آن‌ها توجه کنیم:

اجتناب از Side Effectها

ما نباید منطقی که با داده‌های خارجی، مانند برقراری API callها در تعامل است را مستقیماً در یک کامپوننت reusable قرار دهیم. در عوض، می‌توانیم این منطق را به عنوان

props
props به کامپوننت منتقل کنیم.

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

کدی که در ادامه داریم یک کامپوننت button با قابلیت استفاده مجدد را نشان می‌دهد، اما فاقد بهترین شیوه می‌باشد. دلیل آن را در بخش مثال بررسی خواهیم کرد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// This is a reusable button component (bad practice!)
const Button = () => {
return (
<button> Click Me </button>
);
}
// This is a reusable button component (bad practice!) const Button = () => { return ( <button> Click Me </button> ); }
// This is a reusable button component (bad practice!)
const Button = () => {
  return (
    <button> Click Me </button>
  );
}

استفاده از Props

Props آرگومان‌هایی هستند که ما آن‌ها را برای سفارشی کردن رفتار و ظاهر یک کامپوننت به آن ارسال می‌کنیم. Props به ما این امکان را می‌دهد از یک کامپوننت برای اهداف مختلف استفاده نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// This is a button component that can change its color
const Button = ({ color }) => {
return (
<button style={{ backgroundColor: color }}> Click Here </button>
);
}
// This is a button component that can change its color const Button = ({ color }) => { return ( <button style={{ backgroundColor: color }}> Click Here </button> ); }
// This is a button component that can change its color
const Button = ({ color }) => {
  return (
    <button style={{ backgroundColor: color }}> Click Here </button>
  );
}

این کد هنوز هم بهینه نیست زیرا ما یک برچسب ثابت به نام

Click Here
Click Here داریم. یعنی اگر بخواهیم متن روی دکمه را به
Sign Up
Sign Up تغییر دهیم، باید به کامپوننت button برگردیم و تغییرات را رو آن اعمال کنیم. این بدان معناست هر بار که می‌خواهیم از متن دیگری استفاده کنیم، باید به عقب برگردیم و کد را ویرایش نماییم. به عبارت دیگر، این کامپوننت قابل استفاده مجدد نیست.

نحوه برطرف کردن این مشکل را در بخش بعدی بررسی می‌کنیم.

مثال‌هایی از کامپوننت‌های Reusable در React

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

Buttons

دکمه‌های بیسیک با استایل‌ها و عملکردهای مختلف.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Button component
import React from "react";
const Button = ({ color, label, onClick }) => {
return (
<button
className={`padding-2 shadow-none hover:shadow background-light-${color} hover:background-dark-${color}`}
onClick={onClick}
>
{label}
</button>
);
};
export default Button;
// Using the Button component
<Button color="blue" label="Click Here" onClick={() => console.log("Button clicked!")} />
// Button component import React from "react"; const Button = ({ color, label, onClick }) => { return ( <button className={`padding-2 shadow-none hover:shadow background-light-${color} hover:background-dark-${color}`} onClick={onClick} > {label} </button> ); }; export default Button; // Using the Button component <Button color="blue" label="Click Here" onClick={() => console.log("Button clicked!")} />
// Button component
import React from "react";

const Button = ({ color, label, onClick }) => {
  return (
    <button
      className={`padding-2 shadow-none hover:shadow background-light-${color} hover:background-dark-${color}`}
      onClick={onClick}
    >
      {label}
    </button>
  );
};

export default Button;

// Using the Button component
<Button color="blue" label="Click Here" onClick={() => console.log("Button clicked!")} />

همانطور که می‌بینیم، ما در کامپوننت

button
button متن
Click Here
Click Here را نداریم. قصد داریم دکمه‌ای که داریم قابلیت استفاده مجدد را داشته باشد، بنابراین چیزی در مورد استایل‌ها یا متن‌های سفارشی نمی‌داند. به این ترتیب، آن‌ها را به عنوان props، یعنی
color
color،
label
label و
onClick
onClick ارسال می‌کنیم تا در آینده بدون ایجاد تغییر در کامپوننت اصلی button، آن‌ها را تغییر دهیم.

راه حل: ما باید هر یک از عملکردها را به عنوان

props
props در کامپوننت‌هایی که قابلیت استفاده مجدد دارند، منتقل کنیم.

Navbars

Navigation barهایی که navigation ثابتی را در وب‌سایت ما ارائه می‌دهند.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Navbar component
import React from "react";
const Navbar = ({ isLoggedIn }) => {
return (
<div className="navbar">
<div className="navbar-container">
<div className="navbar-logo">
<img src={logo} alt="logo" />
</div>
<div className="navbar-links">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
{isLoggedIn ? (
<a href="/profile">Profile</a>
) : (
<a href="/login">Login</a>
)}
</div>
</div>
</div>
);
};
export default Navbar;
// Using the Navbar component
<Navbar isLoggedIn={true} />
// Navbar component import React from "react"; const Navbar = ({ isLoggedIn }) => { return ( <div className="navbar"> <div className="navbar-container"> <div className="navbar-logo"> <img src={logo} alt="logo" /> </div> <div className="navbar-links"> <a href="/">Home</a> <a href="/about">About</a> <a href="/contact">Contact</a> {isLoggedIn ? ( <a href="/profile">Profile</a> ) : ( <a href="/login">Login</a> )} </div> </div> </div> ); }; export default Navbar; // Using the Navbar component <Navbar isLoggedIn={true} />
// Navbar component
import React from "react";

const Navbar = ({ isLoggedIn }) => {
  return (
    <div className="navbar">
      <div className="navbar-container">
        <div className="navbar-logo">
          <img src={logo} alt="logo" />
        </div>
        <div className="navbar-links">
          <a href="/">Home</a>
          <a href="/about">About</a>
          <a href="/contact">Contact</a>
          {isLoggedIn ? (
            <a href="/profile">Profile</a>
          ) : (
            <a href="/login">Login</a>
          )}
        </div>
      </div>
    </div>
  );
};

export default Navbar;

// Using the Navbar component
<Navbar isLoggedIn={true} />

همانطور که در کد می‌بینیم،

<Navbar isLoggedIn={true} />
<Navbar isLoggedIn={true} /> را ارسال می‌کنیم.

این خط، از کامپوننت

Navbar
Navbar استفاده کرده و prop
isLoggedIn
isLoggedIn را با مقدار
true
true ارسال می‌کند، که نشان می‌دهد کاربر وارد شده است. در نتیجه لینک Profile را نمایش می‌دهد و لینک Login را پنهان می‌کند.

چرا استفاده از API Callها در کامپوننت Button بهینه نیست؟

اکنون همه چیز را در مورد کامپوننت reusable در React می‌دانیم. قصد داریم تا با حل یک مشکل پیچیده، کمی در این موضوع عمیق‌تر شویم.

سناریویی را در نظر می‌گیریم که در آن دکمه‌ای داریم که API call را انجام می‌دهد. کد کامپوننت button می‌تواند به صورت زیر باشد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import React from "react";
import doAPICall from "../api"
const SaveButton = () => {
return (
<button
onClick={() => {
doAPICall();
}}
>
Save
</button>
);
}
export default SaveButton
import React from "react"; import doAPICall from "../api" const SaveButton = () => { return ( <button onClick={() => { doAPICall(); }} > Save </button> ); } export default SaveButton
import React from "react"; 
import doAPICall from "../api"

const SaveButton  = () => {
  return (
    <button
      onClick={() => {
        doAPICall();
      }}
    >
      Save
    </button>
  );
}

export default SaveButton

کاملاً واضح است که نمی‌توانیم از دکمه بالا در مکان‌های مختلف مجددا استفاده کنیم زیرا این کامپوننت، یک side-effect به نام

doAPICall()
doAPICall() در داخل خود دارد.

می‌توانیم این کامپوننت را reusable کنیم. ابتدا باید side-effect را extract کنیم و آن را به عنوان یک prop به کامپوننت button مانند زیر منتقل نماییم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const App = () => {
function doAPICall() {
// Does an API call to save the current state of the app.
}
return (
<div>
<SaveButton onClick={doAPICall}/>
</div>
)
}
const App = () => { function doAPICall() { // Does an API call to save the current state of the app. } return ( <div> <SaveButton onClick={doAPICall}/> </div> ) }
const App = () =>  {
  function doAPICall() {
    // Does an API call to save the current state of the app.
  }

  return (
    <div>
      <SaveButton onClick={doAPICall}/>
    </div>
  )
}

کامپوننت button باید به شکل زیر باشد:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const SaveButton = ({
onClick
}) => {
return (
<button
onClick={onClick}
>
Save
</button>
);
}
const SaveButton = ({ onClick }) => { return ( <button onClick={onClick} > Save </button> ); }
const SaveButton  = ({
  onClick
}) => {
  return (
    <button
      onClick={onClick}
    >
      Save
    </button>
  );
}

همانطور که می‌بینیم، اکنون از دکمه بالا می‌توانیم در هر جایی که قصد داریم با یک کلیک داده‌ها را ذخیره کنیم، مجددا استفاده نماییم. به عنوان مثال:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const App = () => {
function saveUser() {
// Does an API call to save the user.
}
function saveProject() {
// Does an API call to save the project.
}
return (
<div>
<SaveButton onClick={saveUser}/>
<SaveButton onClick={saveProject}/>
</div>
)
}
const App = () => { function saveUser() { // Does an API call to save the user. } function saveProject() { // Does an API call to save the project. } return ( <div> <SaveButton onClick={saveUser}/> <SaveButton onClick={saveProject}/> </div> ) }
const App = () =>  {
  function saveUser() {
    // Does an API call to save the user.
  }

  function saveProject() {
    // Does an API call to save the project.
  }

  return (
    <div>
      <SaveButton onClick={saveUser}/>
      <SaveButton onClick={saveProject}/>
    </div>
  )
}

همچنین می‌توانیم با استفاده از یک porp برای کنترل label، قابلیت reusable بودن کامپوننت button را بیشتر کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const App = () => {
function saveUser() {
// Does an API call to save the user.
}
function saveProject() {
// Does an API call to save the project.
}
return (
<div>
<SaveButton onClick={saveUser} label="Save user" />
<SaveButton onClick={saveProject} label="Save project" />
</div>
)
}
const App = () => { function saveUser() { // Does an API call to save the user. } function saveProject() { // Does an API call to save the project. } return ( <div> <SaveButton onClick={saveUser} label="Save user" /> <SaveButton onClick={saveProject} label="Save project" /> </div> ) }
const App = () =>  {
  function saveUser() {
    // Does an API call to save the user.
  }

  function saveProject() {
    // Does an API call to save the project.
  }

  return (
    <div>
      <SaveButton onClick={saveUser} label="Save user"  />
      <SaveButton onClick={saveProject} label="Save project" />
    </div>
  )
}

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const SaveButton = ({
onClick,
label
}) => {
return (
<button
onClick={onClick}
>
{label}
</button>
);
}
const SaveButton = ({ onClick, label }) => { return ( <button onClick={onClick} > {label} </button> ); }
const SaveButton  = ({
  onClick,
  label
}) => {
  return (
    <button
      onClick={onClick}
    >
      {label}
    </button>
  );
}

جمع‌بندی

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

باید این موضوع را به خاطر داشته باشیم، کامپوننت‌های reusable بلاک‌های سازنده توسعه قوی React هستند. با تمرین این کامپوننت‌ها می‌توانیم برنامه‌های React تمیزتر، کارآمدتر با قابلیت نگه‌داری بالاتر بسازیم.