قسمت 3: ساختار کامپوننت – ساخت اجزای قابل استفاده مجدد و قابل نگهداری در React!

به قسمت 3 ما خوش آمدید «بهترین شیوههای واکنش در سال 2023» سلسله! در این بخش، اهمیت ساختار مولفه و چگونگی کمک آن به ایجاد اجزایی که بسیار قابل استفاده مجدد، مدولار و نگهداری آسان هستند را بررسی خواهیم کرد.
ساختن کامپوننتهای قابل استفاده مجدد و قابل نگهداری در React فقط نوشتن کد نیست. این در مورد اتخاذ بهترین شیوه ها و پیروی از اصول صحیح معماری است.
با ساختاربندی دقیق اجزای خود، با رعایت موارد اصل مسئولیت واحد، و پذیرفتن مفاهیمی مانند طراحی اتمی و ترکیب کامپوننت، می توانیم کدی ایجاد کنیم که بیشتر باشد مدولار، تست کردن راحت تر، و نگهداری ساده تر.
این رویکرد منجر به فرآیند توسعه کارآمدتر می شود و در نهایت به نتیجه می رسد کیفیت بالا، برنامه های React مقیاس پذیر.
بیایید مثالی را در نظر بگیریم که در آن یک برنامه Todo در React پیاده سازی شده است.
// ❌ Bad code with multiple responsibilities
import React, { useState } from 'react';
const TodoApp = () => {
// Handling state ❌
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState('');
// Handle input change ❌
const handleInputChange = (e) => {
setNewTodo(e.target.value);
};
// Handle todo logic ❌
const handleAddTodo = () => {
if (newTodo.trim() !== '') {
const updatedTodos = [...todos, newTodo];
setTodos(updatedTodos);
setNewTodo('');
}
};
const handleDeleteTodo = (index) => {
const updatedTodos = todos.filter((_, i) => i !== index);
setTodos(updatedTodos);
};
const handleCompleteTodo = (index) => {
const updatedTodos = todos.map((todo, i) => {
if (i === index) {
return { ...todo, completed: !todo.completed };
}
return todo;
});
setTodos(updatedTodos);
};
// ❌ It doesn't provide a clear separation of smaller reusable components.
return (
<div>
<h1>Todo App</h1>
<input type="text"
value={newTodo} onChange={handleInputChange} />
<button onClick={handleAddTodo}>Add Todo</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>{todo.text}</span>
<button onClick={() => handleDeleteTodo(index)}>Delete</button>
<button onClick={() => handleCompleteTodo(index)}>
{todo.completed ? 'Mark Incomplete' : 'Mark Complete'}
</button>
</li>
))}
</ul>
</div>
);
};
پایگاه کد فوق شامل یک مؤلفه واحد است که همه چیز را از آن مدیریت می کند رندر کردن رابط کاربری به مدیریت داده ها و مدیریت دولتی. این رویکرد یکپارچه منجر به عدم تفکیک نگرانی ها و نقض آن می شود SRP و اصول طراحی اتمی.
برای بهبود کد، میتوانیم از اصول SRP و Atomic Design پیروی کنیم:
اصل مسئولیت واحد (SRP)
این اصل بیان می کند که یک کلاس یا جزء باید دارای a باشد مسئولیت واحد یا تنها دلیل برای تغییر. با متمرکز نگه داشتن اجزا بر روی الف وظیفه خاص، کد را بهبود می بخشید خوانایی، قابلیت نگهداری، و قابلیت استفاده مجدد.
این امر باعث تجزیه عملکردهای پیچیده به کوچکتر، قطعات متمرکز که درک، آزمایش و نگهداری آسان تر است.
این مؤلفه ها را به داشتن تشویق می کند روشن و مسئولیت های خاص، قابلیت استفاده مجدد و نگهداری آنها را افزایش می دهد.
به اجتناب کمک می کند محکم جفت شده مولفه ها با متمرکز نگه داشتن آنها بر روی وظایف خاص.
بیایید یکپارچه را بشکنیم،
-
ورودی کار: منطق مدیریت ورودی را در یک جداگانه استخراج کنید
useTodoInput
قلاب و جزء سفارشیTodoInput
.
مسئول رسیدگی به ورودی کاربر و افزودن کارهای جدید.
-
لیست برای انجام کار: منطق مدیریت لیست کارها را در یک جداگانه استخراج کنید
useTodoList
قلاب و جزء سفارشیTodoList
.
مسئول ارائه لیست کارها.
-
TodoItem: منطق رندر کردن کارهای تک تک را به یک جداگانه منتقل کنید
TodoItem
جزء.
مسئول ارائه یک مورد انفرادی.
با جدا کردن حالت و رسیدگی به رویداد منطق را به قلاب ها یا اجزای سفارشی، ما اطمینان حاصل می کنیم که هر جزء دارای یک مسئولیت واحد زیر است.
ورودی کار
useTodoInput قلاب سفارشی می تواند مدیریت کند وضعیت ورودی با استفاده از قلاب useState و رسیدگی به رویداد تغییر ورودی
useTodoInput.js
// ✅ Responsible for manage state and UI events
import { useState } from "react";
const useTodoInput = (onAddTodo) => {
const [inputValue, setInputValue] = useState("");
const [disabled, setDisabled] = useState(true);
const handleSubmit = (e) => {
e.preventDefault();
onAddTodo(inputValue);
clearInput();
};
const handleInputChange = (e) => {
const value = e.target.value;
setInputValue(value);
setDisabled(value.trim() === "");
};
const clearInput = () => {
setInputValue("");
setDisabled(true);
};
return {
disabled,
inputValue,
handleInputChange,
handleSubmit
};
};
export { useTodoInput };
با استفاده از قلابهای سفارشی، میتوانیم منطق مدیریت حالت و رویداد را به روشی قابل استفاده مجدد و مدولار محصور کنیم و قابلیت استفاده مجدد و نگهداری کد را ارتقا دهیم.
TodoInput.jsx
کد JSX مربوط به فیلد ورودی، دکمه “Add Todo” و لیست کارها را به فایل JSX جداگانه منتقل کنید.
// TodoInput.jsx
// ✅ Responsible for rendering TodoInput UI
const TodoInput = ({ onAddTodo }) => {
const {
disabled,
inputValue,
handleInputChange,
handleSubmit
} = useTodoInput(onAddTodo);
return (
<form className="todo-input" onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="Add a todo"
/>
<button
className={`add-button ${disabled ? "disabled" : ""}`}
disabled={disabled}
type="submit"
>
Add
</button>
</form>
);
};
با جدا کردن کد JSX به فایلهای جداگانه، میتوانیم پیشرفت کنیم سازمان کد و خوانایی، ساختنش نگهداری راحت تر و ساختار اجزا را درک کنید.
اینجوری باید خودمون رو تقسیم کنیم TodoItem و لیست برای انجام کار.
این رویکرد refactoring با اختصاص مسئولیتهای منفرد به هر مؤلفه به SRP پایبند است، از قلابهای سفارشی برای مدیریت وضعیت و رویداد استفاده میکند و کد JSX را به اجزای قابل استفاده مجدد تفکیک میکند و ماژولار بودن و قابلیت نگهداری را در برنامه React ارتقا میدهد.
در نهایت، ساختار کامپوننت مانند زیر خواهد بود،
// ✅ Component Stucture
components/
├── todo-input/
│ ├── TodoInput.jsx
│ ├── useTodoInput.js
│ └── TodoInput.css
├── todo-item/
│ ├── TodoItem.jsx
│ └── TodoItem.css
├── todo-list/
│ ├── TodoList.jsx
│ ├── useTodoList.js
│ └── TodoList.css
└── ...
میتوانید کل پایگاه کد را در Codesandbox بررسی کنید.
ما میتوانیم این پایگاه کد را با استفاده از اصول طراحی اتمی بازسازی کنیم.
اصول طراحی اتمی
طراحی اتمی روشی برای طراحی و سازماندهی اجزاء در یک است شیوه سلسله مراتبی بر اساس سطح انتزاع آنها و پیچیدگی.
این مولفه ها را به پنج سطح طبقه بندی می کند: اتم ها، مولکول ها، ارگانیسم ها، الگوها و صفحات، که هر سطح مسئولیت خاصی دارد.
- اتم ها: در پایینترین سطح، اتمها کوچکترین و اساسیترین عناصر رابط کاربری، مانند دکمهها، ورودیها یا نمادها را نشان میدهند.
آنها یک مسئولیت واحد دارند و بر ظاهر بصری و عملکرد اساسی خود تمرکز می کنند.
- مولکول ها: مولکول ها ترکیبی از اتم ها هستند که با هم کار می کنند تا عناصر رابط کاربری پیچیده تری ایجاد کنند.
آنها یک سطح کمی بالاتر از مسئولیت دارند که نشان دهنده گروهی از اتم های مرتبط هستند.
- ارگانیسم ها: ارگانیسم ها از مولکول ها و اتم ها تشکیل شده اند که بخش های بزرگتر و مستقل تری از یک رابط کاربری را نشان می دهند.
آنها رفتار پیچیده تری دارند و ممکن است شامل مدیریت دولت و منطق تعامل باشد.
- قالب ها: الگوها ترتیبات خاصی از موجودات هستند که ساختاری اساسی برای یک صفحه یا بخش ارائه می کنند.
آنها چیدمان و ترکیب کلی UI را تعریف می کنند.
- صفحات: صفحات نمونه هایی هستند که در آن قالب ها با داده های واقعی پر می شوند و محتوای واقعی را برای تعامل کاربر ایجاد می کنند.
بیایید یک مثال از همان برنامه todo را در نظر بگیریم. من یک طراحی کد سطح بالا را با استفاده از الگوی طراحی اتمی ارائه خواهم کرد:
اتم ها
Atoms شامل اجزای UI کوچک و قابل استفاده مجدد مانند دکمه و ورودی.
// ✅ Atoms
// Button.jsx
const Button = ({ onClick, children }) => {
return (
<button className="button" onClick={onClick}>
{children}
</button>
);
};
//Input.jsx
const Input = ({ value, onChange }) => {
return (
<input className="input" type="text" value={value} onChange={onChange} />
);
};
هر اتم فایل جاوا اسکریپت خود را دارد (Button.jsx
، Input.jsx
) و فایل CSS (Button.css
، Input.css
).
مولکول ها
دایرکتوری مولکول ها حاوی ترکیبی از اتم ها (Button.jsx) است که اجزای پیچیده تری را تشکیل می دهند، مانند TodoItem جزء.
// ✅ Molecules
// TodoItem.jsx
const TodoItem = ({ todo, onDelete, onComplete }) => {
return (
<li className="todo-item">
<span className={todo.completed ? 'completed' : ''}>{todo.text}</span>
<Button onClick={onDelete}>Delete</button>
<Button onClick={onComplete}>
{todo.completed ? 'Mark Incomplete' : 'Mark Complete'}
</Button>
</li>
);
};
این فایل جاوا اسکریپت خود را دارد (TodoItem.js) و فایل CSS (TodoItem.css).
ارگانیسم ها
دایرکتوری ارگانیسم ها شامل اجزای بزرگتر و غنی تر است، مانند TodoForm و لیست برای انجام کار اجزاء.
// ✅ Organisms
// TodoForm.jsx
const TodoForm = ({ onAddTodo }) => {
const {inputChange, addTodo} = useTodoForm();
return (
<div className="todo-form">
<Input value={newTodo} onChange={inputChange} />
<Button onClick={addTodo}>Add Todo</Button>
</div>
);
};
// TodoList.jsx
const TodoList = ({ todos, onDeleteTodo, onCompleteTodo }) => {
return (
<ul className="todo-list">
{todos.map((todo, index) => (
<TodoItem
key={index}
todo={todo}
onDelete={() => onDeleteTodo(index)}
onComplete={() => onCompleteTodo(index)}
/>
))}
</ul>
);
};
آنها تشکیل شده اند مولکول ها و/یا اتم ها و JSX (TodoForm.jsx، TodoList.jsx)، قلابهای سفارشی (useTodoForm.js) و خود را دارند. CSS فایل ها.
قالب ها
قالب ها شامل اجزایی هستند که ساختار کلی یک صفحه یا طرح بندی را ارائه می کنند. در این مورد، همه قالب مسئول رندر کردن است TodoForm و لیست برای انجام کار اجزاء.
// ✅ Templates
// Todo.jsx
const Todo = () => {
const {
todos,
addTodo,
deleteTodo,
completeTodo
} = useTodo();
return (
<div className="todo-app">
<h1>Todo App</h1>
<TodoForm onAddTodo={addTodo} />
<TodoList
todos={todos}
onDeleteTodo={deleteTodo}
onCompleteTodo={completeTodo}
/>
</div>
);
};
این فایل JSX خود را دارد (Todo.jsx
) و هوک سفارشی (useTodo.js
) و فایل CSS (Todo.css
).
صفحات
اجزای دایرکتوری صفحات که نمایانگر یک صفحه خاص در برنامه هستند. در این مثال، یک وجود دارد صفحه نخست جزء که به عنوان نقطه ورود اصلی برنامه Todo عمل می کند.
// ✅ Pages
// HomePage.js
const HomePage = () => {
return (
<div className="home-page">
<TodoApp />
</div>
);
};
این مثال نشان می دهد که چگونه پایگاه کد برنامه Todo را می توان با استفاده از الگوی طراحی اتمی ساختار داد. هر جزء مسئول یک نگرانی واحد است و میتوان آنها را بهراحتی مجدداً مورد استفاده قرار داد و آنها را برای ساخت برنامه کامل Todo ترکیب کرد.
افکار نهایی
هنگام طراحی برنامه React خود، اجتناب از اختصاص چندین مسئولیت به یک مؤلفه ضروری است. در اینجا چند استراتژی عملی وجود دارد که به شما کمک می کند تا به یک پایگاه کد تمیزتر و قابل نگهداری تر برسید:
1. مسئولیت های روشن را مشخص کنید: هدف هر جزء را به وضوح مشخص کنید. عملکردهای پیچیده را به اجزای کوچکتر و متمرکز با مسئولیت های کاملاً مشخص تقسیم کنید.
2. تفکیک نگرانی ها: نگرانی ها را با تقسیم برنامه خود به اجزای مجزا بر اساس عملکرد آنها جدا کنید. هر جزء باید نقش خاصی داشته باشد و مسئولیت واحدی را بر عهده بگیرد.
3. ترکیب اجزا: به جای ساخت اجزای بزرگ که وظایف متعددی را انجام می دهند، رابط کاربری خود را با ترکیب اجزای کوچکتر و قابل استفاده مجدد بسازید. این قابلیت استفاده مجدد و مدولار بودن را ارتقا می دهد.
4. توابع تک وظیفه: استخراج منطق پیچیده از اجزا به توابع جداگانه یا ماژول های ابزار. با کپسوله کردن عملکردهای خاص در توابع جداگانه، اجزای خود را بر روی رندر و وظایف مرتبط با رابط کاربری متمرکز می کنید.
5. از اصول SOLID پیروی کنید: به اصول SOLID پایبند باشید، مانند اصل مسئولیت واحد (SRP)، که بیان می کند که یک جزء باید تنها یک دلیل برای تغییر داشته باشد. این اصل به شما کمک می کند تا اجزایی را طراحی کنید که متمرکز، قابل نگهداری و آزمایش آسان تر باشند.
6. از قلاب های سفارشی استفاده کنید: منطق مشترک را در قلاب های سفارشی استخراج کنید که می توانند در بین اجزا به اشتراک گذاشته شوند. این به شما امکان می دهد تا از منطق بدون وارد کردن پیچیدگی غیر ضروری به اجزای جداگانه استفاده مجدد کنید.
7. معماری مدولار: پایگاه کد خود را با استفاده از یک معماری مدولار، مانند ساختار پوشه مبتنی بر ویژگی، سازماندهی کنید. این رویکرد جداسازی نگرانیها را ترویج میکند و کمک میکند تا مؤلفهها بر مسئولیتهای خاص خود متمرکز شوند.
با طراحی آگاهانه برنامه React خود با در نظر گرفتن این شیوهها، میتوانید از انتساب چندین مسئولیت به مؤلفهها اجتناب کنید. این منجر به کد تمیزتر و قابل نگهداری تر می شود که درک، آزمایش و گسترش آن آسان تر است.
پاداش – سلسله مراتب اجزا
به طور کلی توصیه می شود برای حفظ ثبات و خوانایی در پایگاه کد خود از سلسله مراتب خاصی پیروی کنید.
// ✅ Component Hierarchy
// External dependencies
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
// Internal dependencies
import { TodoItem } from './TodoItem';
import { TodoUtils } from '../utils';
import { useTodo } from '../hooks';
import { withTimer } from '../hoc';
import { TodoType } from '../enums';
// Stylesheets
import './Component.css';
import '../styles/common.css';
// Assets
import todoImage from '../assets/todoImage.png';
const Todo = () => {
// State logic
const [todos, setTodos] = useState([]);
// Ref
const inputRef = useRef(null);
// Variable
const title = 'Todo List';
// Custom hook
const {addTodo} = useTodo();
// Higher-order component
const timer =
withTimer(TodoItem);
// Component lifecycle methods (useEffect)
useEffect(() => {
//...
}, []);
// Component render
return (
<div>
{/* Component JSX */}
</div>
);
}
Todo.propTypes = {
// Prop types declaration
};
export { Todo };
با ساختاردهی سلسله مراتب اجزای خود به شیوه ای سازگار و سازماندهی شده، می توانید خوانایی، قابلیت نگهداری و مقیاس پذیری برنامه React خود را بهبود بخشید.
یک سلسله مراتب به خوبی تعریف شده به توسعه دهندگان کمک می کند تا در پایگاه کد حرکت کنند، روابط مؤلفه ها را درک کنند و تغییرات را به طور مؤثر انجام دهند.
منتظر نکات و ترفندهای بیشتر در مورد ساخت برنامه های React با کیفیت بالا در پست های وبلاگ آینده من باشید!
کد نویسی مبارک!😊👩💻👨💻