Skip to content

Project 6 Routing

Theethawat Savastham edited this page Apr 25, 2023 · 7 revisions

Routing with React

จริงๆ แล้ว React เป็นแบบ Single Page App คือทั้งแอพรันบนหน้าเดียว แต่เราก็ยังสามารถประยุกต์ใช้ React กับ Library ต่างๆ เพื่อให้มันสามารถที่จะทำ Virtual Link ภายในได้ โดยในที่นี้เราจะใช้ Library ของ React-Router

  1. ติดตั้ง React Router
npm install --save react-router react-router-dom
  1. Import Component ของ React Router ไปที่ App.js โดยตัวสำคัญๆ จะมี BrowserRouter, Routes และ Route โดยเป็นลักษระของการเป็น context เป็นชั้นๆ ไป
import { Routes, Route } from "react-router";
import { BrowserRouter } from "react-router-dom";

function App(){
...
return (
return (
    <div>
      ...
      <BrowserRouter basename='/'>
        <Routes>
          <Route path='/' element={<div>home</div>} />
          <Route path='/hi' element={<div>hi</div>} />
        </Routes>
      </BrowserRouter>
       ....
</div>
)
}

ลองดูผลการรัน ในหน้าแรกจะเห็นว่ามีคำว่า home ขึ้นมา ขณะที่เมื่อเราเปลี่ยนข้างบน Address bar ใส่ /hi เข้าไป มันจะเป็นคำว่า hi image

ทำ React Router ให้เป็นตัวหลักของโปรเจกต์

  1. สร้างโฟลเดอร์ views อยู่ใน frontend/src ทำหน้าที่เก็บ Logic ในหน้าต่างๆ เราจะสร้าง 3 หน้าใหญ่ๆ ก่อนคือ รายการพนักงาน เพิ่มพนักงาน และ รายละเอียดพนักงาน โดยตอนนี้เรามีรายการพนักงานกับเพิ่มพนักงานแล้ว แต่ยังอยู่ในหน้าเดียวกัน ส่วนรายละเอียดพนักงานไว้ทำทีหลัง เราจะแยกออกมันเป็นหลายๆ หน้า
  2. ในโฟลเดอร์ views สร้างไฟล์ UserList.js ขึ้นมา (ชื่อไฟล์ตั้งเป็น Parcel Case) แล้วย้าย Logic จาก App.js ในส่วนของการแสดงตารางไปที่ UserList.js ตัวอย่างเช่น
import React, { useState, useEffect } from "react";
import {
  Button,
  Card,
  CardContent,
  Input,
  LinearProgress,
  Table,
} from "@mui/joy";
import axios from "axios";
import _ from "lodash";

function CreateUser() {
  const [searchTerm, setSearchTerm] = useState("");
  const [users, setUsers] = useState([]);
  const [isReady, setIsReady] = useState(false);

  const getAllUser = () => {
    setIsReady(false);
    axios
      .get(`${process.env.REACT_APP_API_URL}/user`)
      .then((res) => {
        setUsers(res?.data?.rows);
        setIsReady(true);
        console.log("User ", res?.data?.rows);
      })
      .catch((error) => {
        console.error("Error", error?.message);
      });
  };

  useEffect(() => {
    getAllUser();
    return () => {};
  }, []);

  const handleDeleteUser = (userId) => {
    axios
      .delete(`${process.env.REACT_APP_API_URL}/user/${userId}`)
      .then((res) => {
        getAllUser();
      })
      .catch((error) => {
        alert(error?.message);
        console.error("Error", error?.message);
      });
  };

  if (!isReady) {
    return (
      <div>
        <LinearProgress />
      </div>
    );
  }

  return (
    <div>
      <div className='min-h-screen'>
        <div className='flex justify-center  flex-wrap'>
          <div className='lg:w-3/4'>
            <Card>
              <CardContent>
                <div>Search Box</div>
                <Input
                  placeholder='Input Some Search Word'
                  onChange={(e) => setSearchTerm(e.target.value)}
                />
                <div>
                  You Search <span className='text-blue-500'>{searchTerm}</span>
                </div>
              </CardContent>
            </Card>
            <div>
              <h3 className='font-bold'>User List</h3>
              <Table>
                <thead>
                  <tr>
                    <th>ลำดับที่</th>
                    <th>ชื่อ</th>
                    <th>แผนก</th>
                    <th>ดำเนินการ</th>
                  </tr>
                </thead>
                {_.map(users, (eachUser, index) => (
                  <tr>
                    <td>{index + 1}</td>
                    <td>{eachUser?.name}</td>
                    <td>{eachUser?.department}</td>
                    <td>
                      <Button
                        color='danger'
                        onClick={() => handleDeleteUser(eachUser?._id)}
                      >
                        ลบ
                      </Button>
                    </td>
                  </tr>
                ))}
              </Table>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

export default CreateUser;

และสร้างไฟล์ CreateUser.js

import React, { useState } from "react";
import { Button, Card, CardContent, Input, LinearProgress } from "@mui/joy";
import axios from "axios";
import { useForm, Controller } from "react-hook-form";

function CreateUser() {
  const [searchTerm, setSearchTerm] = useState("");
  const [isReady, setIsReady] = useState(true);
  const { control, handleSubmit } = useForm();

  const handleCreateUser = (data) => {
    console.log("data", data);
    setIsReady(false);
    axios
      .post(`${process.env.REACT_APP_API_URL}/user`, data)
      .then((res) => {
        axios.get(`${process.env.REACT_APP_API_URL}/user`).then((res) => {
          setIsReady(true);
        });
      })
      .catch((error) => {
        console.error("Error", error?.message);
      });
  };

  if (!isReady) {
    return (
      <div>
        <LinearProgress />
      </div>
    );
  }

  return (
    <div>
      <div className='min-h-screen'>
        <div className='flex justify-center  flex-wrap'>
          <div className='lg:w-3/4 '>
            <div className='my-1 font-semibold text-lg'>เพิ่มพนักงานใหม่</div>
            <Card>
              <CardContent>
                <form onSubmit={handleSubmit(handleCreateUser)}>
                  <div>ชื่อ</div>
                  <Controller
                    name='name'
                    control={control}
                    render={({ field }) => (
                      <Input {...field} placeholder='ชื่อพนักงาน' />
                    )}
                  />
                  <div>แผนก</div>
                  <Controller
                    name='department'
                    control={control}
                    render={({ field }) => (
                      <Input {...field} placeholder='แผนก' />
                    )}
                  />
                  <div>
                    <Button type='submit'>บันทึก</Button>
                  </div>
                </form>
              </CardContent>
            </Card>
          </div>
          <div className='lg:w-3/4'>
            <Card>
              <CardContent>
                <div>Search Box</div>
                <Input
                  placeholder='Input Some Search Word'
                  onChange={(e) => setSearchTerm(e.target.value)}
                />
                <div>
                  You Search <span className='text-blue-500'>{searchTerm}</span>
                </div>
              </CardContent>
            </Card>
            <div></div>
          </div>
        </div>
      </div>
    </div>
  );
}

export default CreateUser;

และเราจะมีไฟล์ที่มีโครงสร้างอย่างนี้ image

App.js ก็จะเหลือประมาณนี้

import React from "react";

import { Routes, Route } from "react-router";
import { BrowserRouter } from "react-router-dom";

import Footer from "./Components/Footer";
import Topbar from "./Components/Topbar";

function App() {
  return (
    <div>
      <Topbar appTitle='IARC Devboard' />{" "}
      <BrowserRouter basename='/'>
        <Routes>
          <Route path='/' element={<div>home</div>} />
          <Route path='/hi' element={<div>hi</div>} />
        </Routes>
      </BrowserRouter>
      <Footer />
    </div>
  );
}

export default App;
  1. Import File ทั้ง 2 Files ใน View เข้ามาที่ App.js และมาเติมเต็ม Router ของเรา โดยเราเอา path='/' ชี้ไปที่รายการพนักงานทุกคน ส่วน '/create' ไปที่ CreateUser
import React from "react";

import { Routes, Route } from "react-router";
import { BrowserRouter } from "react-router-dom";

import Footer from "./Components/Footer";
import Topbar from "./Components/Topbar";

function App() {
  return (
   <div>
      <BrowserRouter basename='/'>
        <Topbar appTitle='IARC Devboard' />{" "}
        <Routes>
          <Route path='/create' element={<CreateOneUser />} />
          <Route path='/' element={<UserList />} />
        </Routes>
        <Footer />
      </BrowserRouter>
    </div>
  );
}

export default App;
  1. ลองเปลี่ยนคำที่หลัง Address Bar ปรับปรุงแล้วดูผล

การลิ้งค์ระหว่าง Route

  1. เข้าไปใน Component ของ NavBar แล้วไปใส่ลิงค์ไปยังหน้าต่าง ๆ แล้วเปลี่ยน tag a href เป็น
import { Link } from "react-router-dom";

function TopBar(){
return <div>
 <li>
                <Link
                  to='/'
                  className='block py-2 pl-3 pr-4 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 md:dark:text-blue-500'
                  aria-current='page'
                >
                  Home
                </Link>
              </li>
              <li>
                <Link
                  to='/create'
                  className='block py-2 pl-3 pr-4 text-gray-900 rounded hover:bg-gray-100 md:hover:bg-transparent md:hover:text-blue-700 md:p-0 dark:text-white md:dark:hover:text-blue-500 dark:hover:bg-gray-700 dark:hover:text-white md:dark:hover:bg-transparent dark:border-gray-700'
                >
                  Create User
                </Link>
              </li>
</div>
}
  1. ลองบันทึก และลองคลิกเพื่อเปลี่ยนไปแต่ละหน้าดู

การใช้ Route Parameters

  1. สร้างหน้าสำหรับทำรายละเอียดพนักงาน โดยเราจะสร้างว่า DetailUser.js ใน views แล้ว import เข้าไปที่ App.js
import React from "react";

function DetailUser() {
  return <div>DetailUser</div>;
}

export default DetailUser;

สร้างเป็น Route ตัวหนึ่ง ซึ่งมี Path ที่มีลักษณะเป็น variable ด้วย โดยเมื่อเราเขียน : ไว้ด้านหน้าฟิลด์ไหน มันจะกลายเป็นตัวแปรทันที เช่น /:variableName จะเป็นตัวแปรชื่อ variableName โดยในที่นี้เราจะทำเป็นตัวแปรชื่อ id ใน Route ของ DetailUser โดยมี Path เป็น /detail/:id

     <Route path='/detail/:id' element={<DetailUser />} />

จากนั้นถ้าเรามาที่หน้า detail เฉยๆ มันจะไม่ได้มาที่ Route ของเรา image ขณะที่เมื่อเราใส่ ID เป็นอะไรสักอย่าง ก็จะออกมาที่หน้า Detail User image

  1. ใส่ลิงค์ รายละเอียดไปที่ตารางของ User List และใส่ลิงค์ ที่เอามาจาก react-router-dom มาครอบ Button อีกที โดยในลิงค์เราส่ง ID ไปด้วย
import { Link } from "react-router-dom";
   <Link to={`/detail/${eachUser?._id}`}>
       <Button>รายละเอียด</Button>
   </Link>

image

  1. คลิกปุ่มรายละเอียด เราจะมีหน้า Detail User ที่มี path parameter id ตามที่เราส่งมา
  2. ใน DetailUser ดึงพารามิเตอร์ useParams ออกมาจาก react-router หรือ react-router-dom ก็ได้ สร้างตัวแปร params จาก useParams() จากนั้นลอง console.log params ออกมา
import React from "react";
import { useParams } from "react-router";

function DetailUser() {
    const params = useParams();

    console.log("params", params);
  return <div>DetailUser</div>;
}

export default DetailUser;

จะเห็น parameter ต่างๆ ลิสต์ออกมา โดยของเรา เราจะมีตัวเดียว ก็คือ id ซึ่งจะเป็น id ตัวเดียวกันกับที่ปรากฏบน Address bar ด้านบน image

  1. สร้าง useEffect ทำหน้าที่การไปหาข้อมูล User คนเดียวออกมา โดยใช้ findOne หรือส่ง GET พร้อมกับ id ไปยัง Backend
const params = useParams();
  const [isReady, setIsReady] = useState(false);
  const [data, setData] = useState({});

  useEffect(() => {
    axios
      .get(`${process.env.REACT_APP_API_URL}/user/${params.id}`)
      .then((res) => {
        setData(res.data);
        setIsReady(true);
      });

    return () => {};
  }, [params]);

เราก็สามารถเอา data มาใช้แสดงข้อมูลต่าง ๆ ได้

General

Project 1 Familiar with React

Project 2 Tailwind CSS, Component and Layouting

Project 3 useState, useEffect, React Hook and API Call

Project 4 Basic Express, API and Database Access

Project 5 Frontend Backend Integration

Project 6 Routing

Project 7 Search with react

Project 8 Pushing the Array

Project 9 More complex data structure

Project 10 Mongo Aggregate Pipeline

Project 11 Create Pagination and Search

Clone this wiki locally