یکی از ویژگی های جدیدی که در نسخه های اخیر react اضافه شده است، react hooks می باشد. react hooks به ما کمک می کنند تا با کامپوننت های خود به روش بهتری تعامل داشته باشیم. در این دوره آموزشی یاد گرفتیم که کامپوننت های ما یا functional (کاربردی) بوده و یا class-based (کلاس-محور) هستند. کامپوننت های کاربردی prop هایی را دریافت کرده یا ارسال کنند و کدهای JSX را برمی گردانند و برای نمایش محتوا عالی هستند اما دسترسی به state ندارند به همین خاطر هر کامپوننت کاربردی معمولا فقط روی یک هدف خاص کار می کند. در طرف دیگر کامپوننت های کلاس-محور را داشتیم که دارای state بودند و از prop ها نیز استفاده می کردند و به همین خاطر دیگر کامپوننت ها را مدیریت می کردند. همچنین کامپوننت های کلاس-محور تنها کامپوننت هایی بودند که به lifecycle hooks دسترسی داشتند (با react hooks یکی نیستند).
حتما برای شما هم پیش آمده است که از کامپوننت های کاربردی استفاده می کنیم اما نهایتا مجبور می شویم آن ها را به class-based تغییر دهیم چرا که به دلیل خاصی نیاز به دسترسی به State داریم. این تغییر آزاردهنده است و زمان ما را تلف می کند. از طرفی استفاده از lifecycle hooks نیز آزار دهنده بود؛ اینکه بین componentDidMount یا componentWillMount و دیگر موارد، lifecycle صحیح را انتخاب کنیم و ... کار نسبتا آزار دهنده ای بود. به دلیل وجود چنین موارد نامطلوبی که می دانید، تصمیم گرفته شد که react hooks معرفی شود. react hooks ها به شما اجازه می دهند که فقط از کامپوننت های کاربردی استفاده کنید و دیگر نیازی به کامپوننت های کلاس محور نباشد! البته هنوز هم می توانید از کامپوننت های کلاس-محور استفاده کنید و react hooks تنها یک گزینه اختیاری است.
react hooks در نهایت توابع جاوا اسکریپتی هستند (کامپوننت نیستند) بنابراین فقط می توانند درون کامپوننت های کاربردی یا react hook های دیگر به کار برده شوند. البته شما می توانید hook های خودتان را هم بنویسید که بعدا در مورد آن صحبت می کنیم. قرار داد برنامه نویسی برای hook ها بدین صورت است که نام react hooks با کلمه use شروع شده و سپس نام hook آورده می شود (مثل useState). همچنین react hook ها قابلیت استفاده چندین باره را دارند بنابراین از تکرار در کدهای ما جلوگیری می کنند.
دو نکته مهم نیز وجود دارد که ممکن است باعث سردرگمی شما بشوند:
برای کار با react hooks یک پروژه برایتان آماده کرده ام که باید آن را دانلود کنید تا بتوانید همراه من کدنویسی کنید. مثل همیشه پس از دانلود و خارج کردن فایل ها از حالت فشرده، ابتدا باید دستور npm install را در ترمینالی درون این پوشه اجرا کنید تا پروژه به طور کامل نصب شود. سپس می توانید با npm start پروژه را در مرورگر خود باز کنید. پروژه ما بسیار ساده بوده و شکل زیر را دارد:
بگذارید ابتدا به صورت خلاصه برایتان توضیح دهم که در این پروژه آماده شده چه چیزهایی داریم. ابتدا فایل index.js را داریم:
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; ReactDOM.render(<App />, document.getElementById('root'));
همانطور که می بینید یک فایل بسیار ساده است که فایل index.css را import می کند. فایل index.css مسئول استایل دهی سراسری به کل پروژه است و چیز خاصی ندارد. محتویات کامپوننت App.js نیز بسیار ساده است:
import React from 'react'; import Ingredients from './components/Ingredients/Ingredients'; const App = props => { return <Ingredients />; }; export default App;
در حال حاضر فقط کامپوننت ingredients را برمی گرداند اما بعدا کدهای بیشتری به آن اضافه خواهیم کرد. کامپوننت ingredients.js نیز به شکل زیر است:
import React from 'react'; import IngredientForm from './IngredientForm'; import Search from './Search'; function Ingredients() { return ( <div className="App"> <IngredientForm /> <section> <Search /> {/* Need to add list here! */} </section> </div> ); } export default Ingredients;
فعلا این کامپوننت یک کامپوننت دیگر به نام ingredientForm را نمایش می دهد که همان فرم ما است. همچنین اگر به ingredientForm نگاهی بیندازید متوجه می شوید که یک فرم ساده را داریم که دارای دو input و یک دکمه submit است:
import React from 'react'; import Card from '../UI/Card'; import './IngredientForm.css'; const IngredientForm = React.memo(props => { const submitHandler = event => { event.preventDefault(); // ... }; return ( <section className="ingredient-form"> <Card> <form onSubmit={submitHandler}> <div className="form-control"> <label htmlFor="title">Name</label> <input type="text" id="title" /> </div> <div className="form-control"> <label htmlFor="amount">Amount</label> <input type="number" id="amount" /> </div> <div className="ingredient-form__actions"> <button type="submit">Add Ingredient</button> </div> </form> </Card> </section> ); }); export default IngredientForm;
من این کامپوننت را با React.memo ساخته ام تا از re-render شدن های بی جهت جلوگیری کنیم (اگر یادتان باشد در فصل های اولیه این دوره آموزشی در ارتباط با react memo توضیحات زیادی دادیم). اگر به کد بالا نگاه کنید می بینید که فقط هنگامی re-render اتفاق می افتد که Ingredients.js تغییر کرده و همچنین prop های جدیدی به IngredientForm ارسال شود. در صورتی که اگر از memo استفاده نکنیم، هر بار که Ingredients.js تغییر کند، کامپوننت IngredientForm نیز re-render می شود که بهینه نیست.
کامپوننت ingredientList قرار است بعدا لیستی نمایش بدهد که فعلا از آن استفاده نکرده ایم چرا که باید ابتدا کدهایش را بنویسیم. کامپوننت search نیز تنها وظیفه نمایش یک نوار جست و جو را دارد که در پروژه می بینید (قسمتی انتهایی برنامه که می گوید Filter by Title). در پوشه UI نیز چند کامپوننت کمکی داریم. به طور مثال Card.js مسئول نمایش یک card است (ایجاد سایه پشت یک مستطیل).
کامپوننت ErrorModal.js نیز مسئول دریافت خطاها می باشد که البته هنوز از آن استفاده نمی کنیم و در جلسات بعد آن را به پروژه متصل خواهیم کرد. اگر به ErrorModal نگاه کنید متوجه می شوید که از react.fragment استفاده کرده ایم که کار همان کامپوننت Aux ما را می کرد و به عنوان یک عنصر ریشه ای عمل می کند تا خطایی دریافت نکنیم. با react.fragment نیز قبلا آشنا شده اید. در نهایت کامپوننت loadingIndicator را داریم که همان spinner ما می باشد.
اگر متوجه شده باشید تمام این کامپوننت ها از نوع کاربردی هستند و هیچ کامپوننت کلاس-محوری نداریم! در حال حاضر پروژه ما هیچ کاری انجام نمی دهد بنابراین در قسمت بعد شروع به کدنویسی این پروژه می کنیم و با اولین hook آشنا خواهیم شد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.