اضافه کردن Routing به پروژه ی وبلاگ

22 بهمن 1399
83-React-The-Complete-Guide

اضافه کردن Routing به پروژه ی وبلاگ

تا این قسمت ساختاردهی پروژه ی مان را تغییر داده ایم و حالا آماده ایم که routing را به صورت عملی به آن اضافه کنیم. همانطور که می دانید فعلا در صفحه ی اصلی پروژه تنها عنوان پست ها را نمایش می دهیم:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        <Posts />
    </div>
);

ما می خواهیم برای آدرس صفحه ی اصلی (در این پروژه همان /localhost:3000) عنصر Posts را نمایش دهم. برای انجام این کار ابتدا وارد فایل Blog.js می شویم و سپس کامپوننت Route را از پکیج react-router-dom وارد می کنیم:

import { Route } from 'react-router-dom';

حالا باید آن را به صورت یک عنصر معمولی JSX وارد پروژه کنیم:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        <Route />
    </div>
);

همانطور که می بینید من این کامپوننت را فعلا جایگزین کامپوننت Posts کرده ام. البته نمی توانیم Route را بدین شکل رها کنیم چرا که این عنصر ویژگی هایی می خواهد که باید آن ها را پاس بدهیم، بنابراین:

<Route path="/" render={() => <h1>Home</h1>} />

این دو prop (یعنی path و render) کلمات کلیدی رزرو شده ای برای این پکیج هستند بنابراین نمی توانید نامشان را تغییر دهید. Path همان آدرسی است که میخواهید عنصر Route در آن فعال شود. من مقدار آن را / گذاشته ام که یعنی در همان صفحه ی اصلی (/localhost:3000) فعال شود. زمانی که Route به path خود برسد و فعال شود تابعی را اجرا می کند که به render داده باشیم. این تابع باید مقداری کد JSX را برگرداند که درون صفحه render می شوند. من فعلا در اینجا یک تگ h1 را قرار داده ام تا فقط نحوه ی اجرا را تست کنیم.

اگر این کد را ذخیره کرده و به مرورگر بروید، تگ h1 را مشاهده خواهید کرد:

تگ < h1> رندر شده در صفحه
تگ < h1> رندر شده در صفحه

اما اگر روی لینک new post (بالای صفحه) کلیک کنید باز هم تگ h1 را مشاهده خواهید کرد؛ با کلیک روی new post آدرس مرورگر به http://localhost:3000/new-post تغییر پیدا می کند اما تگ h1 همچنان نمایش داده می شود. آیا می دانید چرا؟ به دلیل اینکه router ما برای مشخص کردن آدرس مرورگر از path استفاده می کند اما path را به عنوان نقطه ی شروع در نظر میگیرد. مثلا در برنامه ی ما میبیند که path برابر با / است بنابراین هر آدرسی که با / شروع شود شامل این Route خواهد شد. با کلیک روی new post آدرس به  http://localhost:3000/new-post تغییر پیدا می کند اما از نظر path با علامت / شروع شده است و دیگر مهم نیست پس از آن چه چیزی باشد (new-post یا هر چیز دیگری). برای حل این مشکل می توانید از exact استفاده کنید که یک خصوصیت Boolean است:

<Route path="/" exact render={() => <h1>Home</h1>} />

از آنجایی که exact یک Boolean است، حضور آن برابر true و نبودش برابر false است و نیازی نیست مقداری به آن بدهید. در حال حاضر اگر به مرورگر بروید دیگر در صفحه ای به جز صفحه ی اصلی، تگ های h1 را مشاهده نخواهید کرد.

همچنین باید توجه داشته باشید که محدودیتی از استفاده از <Route> ندارید و می توانید هر جا و هر چند بار که خواستید از آن استفاده کنید. مثلا من کد خودمان را به شکل زیر تغییر می دهم تا 2 عدد <Route> داشته باشیم:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        <Route path="/" exact render={() => <h1>Home</h1>} />
        <Route path="/" exact render={() => <h1>Home 2</h1>} />
    </div>
);

حالا اگر به مرورگر برویم هر دو h1 برایمان به نمایش در می آیند:

هر دو تگ < h1> رندر شده در صفحه
هر دو تگ < h1> رندر شده در صفحه

مفهوم پایه ی بحث Routing به همین سادگی است! اما مسئله اینجاست که ما نمی خواهیم مقداری JSX را در مسیر / (صفحه ی اصلی) نمایش دهیم بلکه هدف ما render کردن کامپوننت Posts است. به نظر شما راه حل چیست؟

اول از همه دو عنصر <Route> را کامنت می کنیم تا مزاحم کار ما نباشند:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        {/* <Route path="/" exact render={() => <h1>Home</h1>} />
        <Route path="/" exact render={() => <h1>Home 2</h1>} /> */}
    </div>
);

بله! در react اگر بخواهید کدی را کامنت کنید باید ابتدا قسمت مورد نظر را درون curly braces (علامت های {}) قرار داده و سپس از همان حالت HTML برای کامنت کردن استفاده کنید، یعنی ترکیب /* و */

حالا یک <Route> دیگر می نویسیم اما به جای استفاده از render که برای نمایش JSX بود، از prop ای به نام component استفاده می کنیم که به ما اجازه میدهد کامپوننت های مورد نظرمان را به آن پاس بدهیم:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        {/* <Route path="/" exact render={() => <h1>Home</h1>} />
        <Route path="/" exact render={() => <h1>Home 2</h1>} /> */}
        <Route path="/" exact component={Posts} />
    </div>
);

حالا اگر به مرورگر برویم و صفحه ی اصلی را باز کنیم، تمام پست ها را مشاهده می کنیم:

نمایش پست ها (Posts) با routing
نمایش پست ها (Posts) با routing

حالا که توانستیم یک کامپوننت را بارگذاری کنیم، باید به فکر بقیه ی کامپوننت هایمان باشیم و آن ها را نیز به عنصر Route خود بدهیم. برای انجام این کار ابتدا باید NewPost را وارد Blog.js کنیم:

import NewPost from './NewPost/NewPost';

سپس یک دستور <Route> دیگر می نویسیم:

return (
    <div className="Blog">
        <header>
            <nav>
                <ul>
                    <li><a href="/">Home</a></li>
                    <li><a href="/new-post">New Post</a></li>
                </ul>
            </nav>
        </header>
        {/* <Route path="/" exact render={() => <h1>Home</h1>} />
        <Route path="/" exact render={() => <h1>Home 2</h1>} /> */}
        <Route path="/" exact component={Posts} />
        <Route path="/new-post" component={NewPost} />
    </div>
);

من اینجا exact را حذف کرده ام چون می خواهم هر آدرسی بعد از new-post را (مثلا new-post/1) هم با همان کامپوننت NewPost مدیریت کنم اما اگر شما نظری غیر من دارید می توانید exact را به آن اضافه کنید. حالا اگر به مرورگر برویم با کلیک روی لینک Home پست ها را می بینیم و با کلیک روی لینک New Post فرم نوشتن پست جدید را مشاهده می کنیم.

البته کد ما یک مشکل بزرگ دارد! اگر به آیکون دایره ای شکل کروم برای refresh کردن صفحه نگاه کنید، متوجه می شوید که با هر بار کلیک کردن روی لینک های Home یا New Post تمام صفحه refresh می شود. شاید در حال حاضر از این نظر مشکلی نداشته باشیم اما ممکن است بعدا به مشکل برخورد کنیم چرا که refresh شدن صفحه یعنی اجرای دوباره ی کدهای جاوا اسکریپت! حالا فرض کنید کدهای برنامه در وسط کار refresh شوند، در این حالت کاربر باید دوباره عملیات مورد نظرش را انجام دهد چرا که با هر بار refresh صفحه، state برنامه از دست خواهد رفت.

بنابراین باید کاری کنیم که به جای داشتن لینک های عادی Home و New Post که باعث Refresh شدن صفحه می شوند، لینک هایی داشته باشیم که صفحه را refresh نکرده و به router اجازه بدهند تنها قسمت هایی از برنامه را re-render کند. در قسمت بعد به سراغ این موضوع می رویم.

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

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