در قسمت قبل با prop های ارسال شده از سمت routing آشنا شدیم اما مشکل آنجاست که این prop ها در درخت کامپوننت ها ارث برده نمی شوند. به طور مثال ما کد زیر را در Blog.js داشتیم:
<Route path="/" exact component={Posts} />
این کد به ما prop های routing مربوط را میداد ما اگر وارد کامپوننت posts شویم، می بینیم که کار posts رندر کردن post ها است (در فایل post.js) اما اگر post را تبدیل به یک کامپوننت واقعی کنیم دیگر prop های routing مربوط به آن را نمی بینیم. بنابراین برای این کامپوننت ها که قسمتی از کد JSX یک container هستند، به routing prop ها دسترسی نداریم.
دو راه برای حل این مشکل وجود دارد. راه اول پاس دادن props است. به طور مثال وارد فایل posts.js می شویم و this.props را با اپراتور Spread اضافه می کنیم:
if (!this.state.error) { posts = this.state.posts.map(post => { return <Post key={post.id} title={post.title} author={post.author} {...this.props} clicked={() => this.postSelectedHandler(post.id)} />; }); }
همچنین می توانیم prop خاصی را پاس بدهیم. مثلا اگر یادتان باشد در جلسه ی قبل با prop ای به نام match آشنا شدیم که می توانیم آن را به شکل زیر پاس بدهیم:
if (!this.state.error) { posts = this.state.posts.map(post => { return <Post key={post.id} title={post.title} author={post.author} match={this.props.match} clicked={() => this.postSelectedHandler(post.id)} />; }); }
این یک روش پاس دادن routing props است اما روش دوم استفاده از HOC ها است. برای شروع match را از کد بالا حذف کرده و آن را به حالت عادی خود برگردانید. سپس وارد فایل Post.js شده و یک HOC به نام withRouter را از پکیج react-router-dom وارد پروژه کنید:
import { withRouter } from 'react-router-dom';
برای استفاده از این HOC باید هنگام نوشتن دستور export کامپوننت را درون withRouter قرار دهیم:
export default withRouter(post);
من کدهای فایل Post.js را به حالت قبلی برمی گردانم اما می خواستم شما بدانید که با استفاده از این دو روش می توانید از routing prop ها استفاده کنید.
معمولا زمانی که این بحث مطرح می شود برخی از افراد سوالی در ذهنشان پیش می آید که آیا routing prop ها (مثلا match) استفاده ی واقعی دارند؟
پاسخ این سوال قطعا مثبت است. بگذارید یک مورد را نشانتان بدهم. اگر یادتان باشد آدرس لینک های خود را در فایل Blog.js به شکل زیر تعریف کرده بودیم:
<ul> <li><Link to="/">Home</Link></li> <li><Link to={{ pathname: '/new-post', hash: '#submit', search: '?quick-submit=true' }}>New Post</Link></li> </ul>
در حالت معمول در react آدرس دهی نسبی وجود ندارد! آدرس های مطلق همیشه مستقیما به نام دامنه ی شما متصل خواهند شد بنابراین اگر دامنه ی شما example.com باشد و بخواهید به example.com/new-post بروید باید pathname لینک را روی new-post
یا new-post/
قرار دهید (تفاوتی ندارد که /
باشد یا نباشد). این یعنی همیشه new-post مستقیما به example.com متصل کن. حتی اگر در حال حاضر در آدرس example.com/posts باشید و روی لینک کلیک کنید، به آدرس example.com/posts/new-post منتقل نمی شوید بلکه posts حذف شده و مستقیما به example.com/new-post می روید. به این روش آدرس دهی، آدرس دهی مطلق (absolute path) می گویند و در حالت عادی react همیشه به شکل absolute path عمل می کند اما راهی برای دور زدن این محدودیت وجود دارد. اگر بخواهیم آدرس دهی نسبی (relative path) داشته باشیم باید از شیء match کمک بگیریم! ما می توانیم با استفاده از match بفهمیم که در حال حاضر در چه آدرسی هستیم، سپس آدرس new-post را به آدرس فعلی اضافه کنیم. مثال:
<ul> <li><Link to="/">Home</Link></li> <li><Link to={{ pathname: this.props.match.url + '/new-post', hash: '#submit', search: '?quick-submit=true' }}>New Post</Link></li> </ul>
شیء match خصوصیت url را داشت که آدرس فعلی را به ما میداد بنابراین اگر آن را به new-post متصل کنیم می توانیم یک آدرس نسبی داشته باشیم. این یک استفاده ی ساده از routing props است و استفاده های بیشتر آن واقعا به خود شما و پروژه تان بستگی دارد.
در این بخش می خواهیم کاری کنیم که اگر در آدرس home (صفحه ی اصلی به آدرس http://localhost:3000/) بودیم، دکمه ی Home استایل خاصی بگیرد و یا اگر در آدرس http://localhost:3000/new-post بودیم دکمه ی New Post استایل خاصی بگیرد تا کاربر بداند دقیقا کجاست. برای انجام این کار دیگر نمی توانیم از کامپوننت <Link> استفاده کنیم بلکه باید کامپوننت دیگری به نام <NavLink> را وارد پروژه کنیم بنابراین به فایل Blog.js رفته و دستور import مربوط به Link را به NavLink تغییر دهید:
import { Route, NavLink } from 'react-router-dom';
و حالا تمام <Link> ها را به <NavLink> عوض می کنیم:
<ul> <li><NavLink to="/">Home</NavLink></li> <li><NavLink to={{ pathname: '/new-post', hash: '#submit', search: '?quick-submit=true' }}>New Post</NavLink></li> </ul>
اگر الان به مرورگر رفته و تگ <a> ایجاد شده را نگاه کنید چنین چیزی می بینید:
بله اگر از NavLink استفاده کنیم و در آدرسی باشیم که آن NavLink مشخص کرده باشد، کلاس active به صورت خودکار به آن اضافه می شود. مثلا اگر در آدرس http://localhost:3000/new-post باشیم و دکمه ی New Post را inspect کنیم تصویر بالا را می بینیم. کلاس active فقط به لینک آدرس فعلی اضافه می شود.
حالا می توانیم به فایل Blog.css رفته و آخرین دستور CSS را بدین شکل تغییر دهیم:
.Blog a:hover, a:active, .Blog a.active { color: #fa923f; }
بله علاوه بر کلیک روی لینک و active بودن آن قانون سومی را اضافه کرده ایم که می گوید هر لینکی که کلاس active را دارد نیز باید نارنجی رنگ شود. اگر الان به مرورگر برویم مشکلی را می بینیم. اگر روی Home کلیک کنیم فقط لینک Home نارنجی می شود اما اگر روی New Post کلیک کنیم هر دو لینک New Post و Home نارنجی می شوند. آیا میتوانید دلیلش را حدس بزنید؟
اگر یادتان باشد گفته بودیم که در کد زیر:
<Route path="/" exact component={Posts} />
قسمت path به عنوان پیشوند رفتار می کند مگر آنکه کلید واژه ی exact را روی آن سوار کنیم. این مسئله در مورد لینک ها نیز صادق است بنابراین کد زیر:
<li><NavLink to="/">Home</NavLink></li>
به عنوان یک پیشوند عمل می کند. یعنی لینک Home در هر آدرسی که با /
شروع شود فعال خواهد شد و آدرس new-post/ نیز دارای علامت /
است که باعث فعال شدن Home می شود. راه حل این مشکل بسیار ساده است، باید دوباره از exact استفاده کنیم:
<li><NavLink exact to="/">Home</NavLink></li>
بدین صورت NavLink می فهمد که /
آدرس کامل است نه یک پیشوند برای هر آدرس دیگری که داشته باشیم.
سوال: من برخی اوقات در قسمت های دیگری از برنامه هایم از کلاس active استفاده میکنم که باعث تداخل با این کلاس active می شود. راه چاره چیست؟
پاسخ: برای حل این مشکل سه راه دارید: یا از CSS Module ها استفاده کنید تا کلاس هایتان دارای scope باشد، یا کلاس active اضافه شده را تغییر دهید و یا از استایل های inline استفاده کنید. در مورد CSS Module ها که قبلا توضیح داده ام بنابراین تکرار مکررات نمی کنم اما شما می توانید با استفاده از یک prop به نام activeClassName به react بگویید که به جای اضافه کردن کلاس Active کلاس دیگری را اضافه کند:
<li><NavLink exact to="/" activeClassName="my-active">Home</NavLink></li>
حالا به جای اضافه شدن کلاس Active، کلاس my-active (یا هر چیز دیگری که برایش انتخاب کنید) اضافه خواهد شد. توجه داشته باشید که این مورد فقط برای لینک Home انجام می شود اما برای New Post هنوز هم همان کلاس active اضافه خواهد شد (activeClassName را به آن اضافه نکرده ایم). من این مورد را حذف می کنم اما اگر شما دوست داشتید آن را در برنامه تان تغییر دهید، دستتان باز است.
اما قبل از آنکه activeClassName را حذف کنم می خواهم مورد دیگری را به شما نشان دهم. Prop دیگری به نام activeStyle وجود دارد که به شما اجازه می دهد بدون استفاده از کلاس های CSS و به جای آن استفاده از استایل های inline، لینک مورد نظرتان را استایل دهی کنید. مثال:
<ul> <li><NavLink exact to="/" activeClassName="my-active" activeStyle={{ color: '#fa923f', textDecoration: 'underline' }} >Home</NavLink></li> <li><NavLink to={{ pathname: '/new-post', hash: '#submit', search: '?quick-submit=true' }}>New Post</NavLink></li> </ul>
بنابراین با استفاده از activeStyle می توانید استایل های خود را همانجا و به صورت inline به لینک مورد نظر بدهید، به همین سادگی! در قسمت بعد در رابطه با نحوه ی load کردن پست ها با کلیک روی عنوان آن ها صحبت خواهیم کرد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.