در این مقاله میخواهیم یک مروری بر انواع کامپوننت ها در ری اکت داشته باشیم و ببینیم طی مدت زمانی که ری اکت معرفی شد، چه الگو ها و چه نوع کامپوننت هایی پیش روی برنامه نویسان بوده. هدف از نوشتن این مقاله این هست که ببینیم در حال حاضر و در این دنیای مدرن کدنویسی و ری اکت، چه نوع کامپوننت هایی در دسترس برنامه نویسان هست و چه نوعی از آنها هم در گذشته بوده و امروزه دیگه اهمیتی ندارن و در آخر هم انتظار میره که شما بتونید با دیدن کد اپلیکیشن ها و آموزش های قدیمی تر این کامپوننت های یجورایی منسوخ شده را تشخیص بدید و اونهارو به شیوه نوینشون بازنویسی کنید.
React createClass Components
همه چیز با React createClass Components شروع شد. متد createClass
توسعه دهندگان را قادر به ایجاد یک کلاس کامپوننت بدون استفاده از سینتکس کلاس های جاوااسکریپت میکرد و این متد تنها راه ایجاد یک کلاس کامپوننت بود و دلیلش هم این بود که در اون موقع و قبل از ورژن ES6 یعنی در ES5 هیچ سینتکسی برای ایجاد کلاس موجود نبود.
var App = React.createClass({
getInitialState: function() {
return {
value: '',
};
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
متد createClass
یک آبجکت به عنوان ورودی میگیره و این آبجکت هم به یکسری کلید ها جهت ایجاد کلاس کامپوننت نیاز داره. کلید getInitialState
که در واقع state ما هست و کلید اجباری render
هم که به کار میره تا jsx را بنویسیم. متد های اضافی دیگه هم مثله onChange
وجود دارن تا بتونیم فانکشن های بیشتری را به کامپوننت اضافه کنیم.
همچنین متدهای LifeCycle هم در این کامپوننت ها وجود داشتن تا به عنوان مثال بتونیم با هربار رندر مجدد کامپوننت قطعه کدی که مدنظر داریم را اجرا کنیم.
ar App = React.createClass({
getInitialState: function() {
return {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
},
componentDidUpdate: function() {
localStorage.setItem('myValueInLocalStorage', this.state.value);
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
مثلا در کد بالا با هربار تغییر مقدار input و state اون مقدار را توی localStorage ذخیره میکنیم تا بعد از رفرش شدن صفحه هم مقادیر تایپ شده قبلی وجود داشته باشند.
نکته:به خاطر داشته باشید که متد createClass
دیگه در ری اکت وجود نداره و اگر میخواهید ازش استفاده کنید نیازه اونرو با دستور npm install create-react-class
نصب و به پروژتون اضافه کنید.
در کل هم بهتره از createClass
استفاده نکنید، چراکه این متد منسوخ شده.
React Mixins
React Mixin به عنوان اولین pattern پیشرفته برای قابل استفاده مجدد شدن کامپوننت ها معرفی شد تا این امکان رو به توسعه دهنده ها بده که تا حد خوبی منطق کد خودشون رو از کامپوننت ها جدا کنند.
var localStorageMixin = {
getInitialState: function() {
return {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
},
setLocalStorage: function(value) {
localStorage.setItem('myValueInLocalStorage', value);
},
};
var App = React.createClass({
mixins: [localStorageMixin],
componentDidUpdate: function() {
this.setLocalStorage(this.state.value);
},
onChange: function(event) {
this.setState({ value: event.target.value });
},
render: function() {
return (
<div>
<h1>Hello React "createClass" Component with Mixin!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
},
});
در کد بالا یک Mixin ایجاد کردیم که یک state و یک متد برای setState کردن را به کامپوننت مدنظر ما اضافه میکنه. state ما حاوی مقداری هست که از localStorage خوانده میشه و setState ما هم مقدار جدیدی را در localStorage مینویسه.
همچنین برای منعطف تر و داینامیک تر کردن Mixin میتونیم اونرو تبدیل به فانکشنی کنیم که یک آبجکت را بر میگردونه:
function getLocalStorageMixin(localStorageKey) {
return {
getInitialState: function() {
return { value: localStorage.getItem(localStorageKey) || '' };
},
setLocalStorage: function(value) {
localStorage.setItem(localStorageKey, value);
},
};
}
var App = React.createClass({
mixins: [getLocalStorageMixin('myValueInLocalStorage')],
...
});
هرچند که عمر Mixin ها هم به سر آمده و دیگه از اونها توی اپلیکیشن های مدرن استفاده نمیشه.
React Class Components
کلاس کامپوننت ها همراه با ورژن ES6 جاوااسکریپت معرفی شدن و از اینرو گاهی اوقات اونهارو React ES6 class components هم مینامیم. متد createClass
پس از معرفی کلاس کامپوننت ها از میان برداشته شد و دیگه احتیاجی نیست برای ایجاد یک کلاس کامپوننت از متد یاد شده استفاده کرد.
کلاس کامپوننت های ری اکتی همراه با یسکری متد ها میاد، متدهایی مثل class constructor که برای ایجاد state و یا bind کردن متدها استفاده میشه و یا render که جهت خروجی کار و نمایش jsx بکار میره.
نکته: برای bind شدن خودکار متد ها میتونیم از arrow function ها در کلاس کامپوننت هامون استفاده کنیم.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
};
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>Hello React ES6 Class Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
همچنین در کلاس کامپوننت ها ما یسکری متد lifeCycle برای mounting, updating و unmounting داریم که میتونیم از اونها برای side-effect ها استفاده کنیم.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: localStorage.getItem('myValueInLocalStorage') || '',
};
}
componentDidUpdate() {
localStorage.setItem('myValueInLocalStorage', this.state.value);
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>Hello React ES6 Class Component!</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
لازم به ذکر هست که کلاس کامپوننت ها همچنان در اپلیکیشن های مدرن هم استفاده میشن، هرچند کمی کمرنگ تر، اما همچنان پابرجا هستند و استفاده های خاص خودشون را دارند.
React Higher-Order Components
React Higher-Order Component یا (HOCs) یک راه جایگزین و محبوب برای Mixin ها هستند تا ما بتونیم بوسیله اونها منطق های قابل استفاده مجدد خودمون را بین کامپوننت ها رد و بدل کنیم.
به زبانی ساده تر، Higher-Order Component را به کامپوننتی میگن که به عنوان ورودی یک کامپوننت دریافت میکنه و بعد اون کامپوننت را به عنوان خروجی و با عملکردی بیشتر تحویل میده.
به عنوان مثال نمونه کدی که قبل تر در کلاس کامپوننت نوشتیم تا مقدار input را در localStorage بنویسه و بعد درصورت رفرش شدن به عنوان مقدار اولیه state داشته بخونتش را بیایید با سینتکس React Higher-Order Component ببینیم:
const withLocalStorage = localStorageKey => Component =>
class WithLocalStorage extends React.Component {
constructor(props) {
super(props);
this.state = {
[localStorageKey]: localStorage.getItem(localStorageKey),
};
}
setLocalStorage = value => {
localStorage.setItem(localStorageKey, value);
};
render() {
return (
<Component
{...this.state}
{...this.props}
setLocalStorage={this.setLocalStorage}
/>
);
}
};
class App extends React.Component {
constructor(props) {
super(props);
this.state = { value: this.props['myValueInLocalStorage'] || '' };
}
componentDidUpdate() {
this.props.setLocalStorage(this.state.value);
}
onChange = event => {
this.setState({ value: event.target.value });
};
render() {
return (
<div>
<h1>
Hello React ES6 Class Component with Higher-Order Component!
</h1>
<input
value={this.state.value}
type="text"
onChange={this.onChange}
/>
<p>{this.state.value}</p>
</div>
);
}
}
const AppWithLocalStorage = withLocalStorage('myValueInLocalStorage')(App);
از React Higher-Order Component ها هم همچنان بصورت منظم در اپلیکیشن های مدرن استفاده میشه.
React Function Components
state و متدهای lifeCycle در اوایل راه در فانکشنال کامپوننت ها وجود نداشتند و از اینرو این کامپوننت هارو Functional Stateless Components مینامیدند. اما کار به همینجا ختم نشد و کمی بعد سیستم هوک های ری اکت معرفی شدن و این هوک ها به فانکشنال کامپوننت ها روحی دوباره دادن.
این هوک ها state و lifeCycle هارو مجددا به این نوع از کامپوننت ها اضافه کردند و علاوه بر این دست توسعه دهنده را برای ایجاد هوک های شخصی هم باز گذاشتن.
بریم کد قبلی که در کلاس کامپوننتمون نوشته بودیم را اینبار با فانکشنال کامپوننت بازنویسی کنیم:
const App = () => {
const [value, setValue] = React.useState('');
React.useEffect(() => {
localStorage.setItem('myValueInLocalStorage', value);
}, [value]);
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
در کد بالا از هوک useState برای ذخیره مقدار input استفاده میکنیم و همچنین از هوک useEffect جهت هندل کردن side-effect و نوشتن مقدار جدید با هربار تغییر مقدار input استفاده میکنیم.
همانطور که در کدبالا مشاهده میکنید، ما state خودمون را به این شکل [value]
به عنوان dependency به هوک useEffect
دادیم تا کار آپدیت کردن را برای ما انجام بده و به این شکل به این هوک گفتیم که با هربار تغییر value
بیا کدی که درونت نوشتیم را مجدد اجرا کن و مقدار جدید را در localStorage ذخیره کن.
همچنین میتونیم کاری که در بالا انجام میدیم یعنی state و useEffect خودمون را به یک custom hook ( هوک شخصی ) منتقل کنیم و به این شکل اولین هوک شخصی خودمون را ایجاد کنیم:
const useStateWithLocalStorage = localStorageKey => {
const [value, setValue] = React.useState(
localStorage.getItem(localStorageKey) || '',
);
React.useEffect(() => {
localStorage.setItem(localStorageKey, value);
}, [value]);
return [value, setValue];
};
const App = () => {
const [value, setValue] = useStateWithLocalStorage(
'myValueInLocalStorage',
);
const onChange = event => setValue(event.target.value);
return (
<div>
<h1>Hello React Function Component!</h1>
<input value={value} type="text" onChange={onChange} />
<p>{value}</p>
</div>
);
};
و با این کار میتونیم منطق کد خودمون را بوسیله یک custom hook بین تمامی کامپوننت هامون به اشتراک بگذاریم و کدی قابل استفاده مجدد بنویسیم. سیستم هوک های ری اکت انعطاف پذیری و قدرت خیلی زیادی را به اپلیکیشن های ری اکتی اضافه کردند و بطور کلی ری اکت را متحول کردن و از اینرو ما معتقدیم که استفاده از کلاس کامپوننت ها یروزی بطور کامل کنار خواهد رفت ( هرچند که همین الان هم به شدت کمرنگ شده )