عدم وراثت prop های routing + استفاده از NavLink

22 بهمن 1399
عدم وراثت prop های routing + استفاده از NavLink

عدم وراثت prop های routing

در قسمت قبل با 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 ها استفاده کنید.

آدرس دهی مطلق و نسبی در React

معمولا زمانی که این بحث مطرح می شود برخی از افراد سوالی در ذهنشان پیش می آید که آیا 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 است و استفاده های بیشتر آن واقعا به خود شما و پروژه تان بستگی دارد.

استایل دهی لینک فعال بر اساس URL

در این بخش می خواهیم کاری کنیم که اگر در آدرس 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> ایجاد شده را نگاه کنید چنین چیزی می بینید:

کلاس active اضافه شده به صورت خودکار
کلاس active اضافه شده به صورت خودکار

بله اگر از 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>
استایل دهی به nav menu با استفاده از استایل های inline
استایل دهی به nav menu با استفاده از استایل های inline

بنابراین با استفاده از activeStyle می توانید استایل های خود را همانجا و به صورت inline به لینک مورد نظر بدهید، به همین سادگی! در قسمت بعد در رابطه با  نحوه ی load کردن پست ها با کلیک روی عنوان آن ها صحبت خواهیم کرد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری دوره جامع آموزش ری اکت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.