حالا که با مفهوم Routing آشنا شده ایم باید پکیج Router مورد نظرمان را راه اندازی کنیم. برای این کار ترمینال خود را در صفحه ی اصلی پروژه تان باز کرده و دستور زیر را در آن تایپ کنید:
Npm install --save react-router react-router-dom
بله همانطور که می بینید به دو پکیج متفاوت نیاز داریم:
این دو پکیج توسط فیسبوک ساخته نشده اند اما مشهورترین پکیج برای routing در react می باشند. پس از تمام شدن نصب دو پکیج، دوباره دستور npm start را اجرا کنید تا سرور مجازی شروع به کار کند.
نکته ی بسیار مهم: در ورژن های جدید react-router دیگر نیازی به نصب هر دو پکیج نیست. اگر کار شما فقط توسعه ی وب است (طراحی سایت و مسائل مرتبط با آن) فقط به پکیج react-router-dom نیاز خواهید داشت چرا که react-router را درون خود دارد. من هر دو را به شما نشان دادم تا بدانید در ورژن های قدیمی موضوع فرق میکرده است.
اولین کاری که باید انجام دهیم این است که فقط پست ها (قسمت عناوین پست ها) در صفحه ی اصلی برنامه نمایش داده شوند. در حال حاضر در Blog.js ما تمام موارد را در صفحه ی اصلی نمایش می دهیم:
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> <section className="Posts"> {posts} </section> <section> <FullPost id={this.state.selectedPostId} /> </section> <section> <NewPost /> </section> </div> );
بنابراین دو مورد آخر (FullPost و NewPost) را حذف می کنیم:
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> <section className="Posts"> {posts} </section> </div> );
حالا باید routing را در برنامه ی خودمان فعال کنید؛ شما میتوانید این کار را درون index.js یا App.js انجام دهید. من App.js را انتخاب کرده ام بنابراین وارد این فایل شده و دستور import زیر را به آن اضافه می کنم:
import { BrowserRouter } from 'react-router-dom';
حالا باید تمام برنامه تان را درون این BrowserRouter قرار دهید. بنابراین در همان App.js و در قسمت return آن، div ریشه ای را درون عنصر BrowserRouter قرار می دهم:
return ( <BrowserRouter> <div className="App"> <Blog /> </div> </BrowserRouter> );
حالا می توانیم در تمام قسمت های برنامه ی خود از قابلیت های این پکیج استفاده کنیم.
برای شروع کار دستورات import مربوط به FullPost و NewPost و Post را از فایل Blog.js حذف کنید چرا که نیازی به آن ها نخواهیم داشت. حالا پوشه های FullPost و NewPost را به درون پوشه ی Blog منتقل می کنم. سپس درون همان پوشه ی Blog یک پوشه ی دیگر به نام Posts می سازم که درون خود فایلی به نام Posts.js دارد. سپس از قسمت JSX در فایل Blog.js کدهای مربوط به نمایش Post را کات می کنیم. منظورم این قسمت است:
<section className="Posts"> {posts} </section>
در مرحله ی بعد وارد فایل Posts.js می شویم و آن را به صورت یک کامپوننت کلاس-محور تکمیل می کنیم:
import React, { Component } from 'react'; class Posts extends Component { render() { return ( <section className="Posts"> {posts} </section> ); } }
بله کدهایی که کات کرده بودیم را در این قسمت paste کرده ایم. البته یک مشکل وجود دارد؛ در این فایل هیچ posts ای وجود ندارد چرا که posts به صورت پویا در فایل Blog.js تعریف شده بود. به همین دلیل به Blog.js رفته و کد مربوط به ساخت posts را کات می کنیم و به شکل زیر درون Posts.js قرار می دهیم:
render() { let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>; if (!this.state.error) { posts = this.state.posts.map(post => { return <Post key={post.id} title={post.title} author={post.author} clicked={() => this.postSelectedHandler(post.id)} />; }); } return ( <section className="Posts"> {posts} </section> ); }
مشکل بعدی این است که کد مربوط به ساخت Post به state نیاز دارد (this.state.posts) بنابراین باید کدهای State را از Blog.js به Posts.js ببریم:
state = { posts: [], selectedPostId: null, error: false }
اما به دو قسمت آخر (selectedPostId و error) نیازی نداریم به همین دلیل آن را حذف می کنیم:
state = { posts: [] }
اگر باز هم به قسمت JSX در تعریف متغیر posts نگاه کنید متوجه می شوید که متد postSelectedHandler در فایل Blog.js جامانده است. بنابراین آن را هم به فایل Posts.js بیاورید:
postSelectedHandler = (id) => { this.setState({ selectedPostId: id }); }
در مرحله ی بعد تمام componentDidMount را نیز از فایل Blog.js کات کرده و درون Posts.js قرار دهید:
componentDidMount() { axios.get('/posts') .then(response => { const posts = response.data.slice(0, 4); const updatedPosts = posts.map(post => { return { ...post, author: 'Max' } }); this.setState({ posts: updatedPosts }); // console.log( response ); }) .catch(error => { // console.log(error); this.setState({ error: true }); }); }
از آنجا که error در state را حذف کردیم دیگر نیازی به مدیریت خطا در این قسمت نداریم بنابراین به جای مدیریت آن، فقط console.log می کنیم:
componentDidMount() { axios.get('/posts') .then(response => { const posts = response.data.slice(0, 4); const updatedPosts = posts.map(post => { return { ...post, author: 'Max' } }); this.setState({ posts: updatedPosts }); // console.log( response ); }) .catch(error => { console.log(error); // this.setState({ error: true }); }); }
در نهایت باید axios را درون posts.js وارد کنیم تا بتواند پست های خودش را دریافت کند. به همین خاطر دستور import آن را از Blog.js حذف کرده و دستور import زیر را در Posts.js قرار دهید:
import axios from '../../../axios';
همچنین Posts.js از Post.js استفاده می کند که باید آن را هم درون آن import کنیم:
import Post from '../../../components/Post/Post';
حالا کامپوننت Posts.js کامل شده است و پست های خودش را دریافت می کند بنابراین می توانیم به فایل Blog.js رفته و Posts.js را در آن import کنیم:
import Posts from './Posts/Posts';
سپس در قسمت JSX فایل Blog.js می گوییم:
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> );
همانطور که مشاهده میکنید <Posts /> را در آن ایجاد کرده ایم.
چند مورد دیگر برای تکمیل کدها باقی مانده است. یادتان باشد کلاس Posts را در انتهای فایل خود (Posts.js) استخراج کنید:
export default Posts;
همچنین کلاس Posts را از فایل Blog.css کات کنید:
.Posts { display: flex; flex-flow: row wrap; justify-content: center; width: 80%; margin: auto; }
سپس درون پوشه ی Posts یک فایل به نام Posts.css بسازید و کلاس بالا را در آن paste کنید. حالا به فایل Posts.js می رویم تا این کلاس را در آن import کنیم:
import './Posts.css';
توجه داشته باشید که از CSS Module ها استفاده نکرده ایم بنابراین نیازی به تعیین نام برای دستور import بالا وجود ندارد. کلاس ها سراسری هستند و باید مستقیما نیز وارد پروژه شوند.
حالا پروژه ی ما آماده ی اضافه کردن Routing می باشد که در جلسه ی بعد کار های مربوط به آن را شروع خواهیم کرد.
در این قسمت، به پرسشهای تخصصی شما دربارهی محتوای مقاله پاسخ داده نمیشود. سوالات خود را اینجا بپرسید.