react.dev
Create react project
Run the following command in your terminal: vite dev/guide/
npm create vite@latest
tanstack/react-query
Hooks for fetching, caching and updating asynchronous data in React, Solid, Svelte and Vue
npm i @tanstack/react-query
react-query-devtools
npm i @tanstack/react-query-devtools
Tailwind CSS with Vite
tailwindcss com/docs/guides/vite
Install npm i uuid
my-app\src\App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //src\App.jsx import { Route, Routes } from "react-router-dom" //npm i react-router-dom import Home from "./pages/Home" import Post from "./pages/Post" function App() { return ( <Routes> <Route path= "/" element={<Home />} /> <Route path= "/post/:slug" element={<Post />} /> </Routes> ) } export default App |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | //src\main.jsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query" ; import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import './index.css' import { BrowserRouter } from "react-router-dom" ; import App from './App.jsx' // create a client const queryClient = new QueryClient(); createRoot(document.getElementById( "root" )).render( <StrictMode> <BrowserRouter> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </BrowserRouter> </StrictMode>, ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | //src\pages\Home.jsx import { useQuery } from "@tanstack/react-query" ; //npm i @tanstack/react-query import { getPostList } from "../api/post" ; import Pagination from "../components/Pagination" ; import { useSearchParams } from "react-router-dom" import PostList from "./PostList" ; export default function Home() { const [searchParams] = useSearchParams(); const page = Number(searchParams.get( "page" )); //console.log(page) const currentPage = Number(page) || 1; //console.log(currentPage) const { isLoading, data, isError, error } = useQuery({ queryKey: [ "customers" , currentPage], queryFn: () => getPostList(currentPage) }); //console.log(data); if (isLoading) return "Loading..." ; if (isError) return `Error: ${error.message}`; const totalPages = Math. ceil (Number(data.totalpage) / Number(data.perpage)); console.log(totalPages); return ( <div className= "w-screen py-20 flex justify-center flex-col items-center" > <div className= "flex items-center justify-between gap-1 mb-5 pl-10 pr-10" > <h1 className= "text-4xl font-bold" >Reactjs 18 WordPress Rest API Pagination View Post | Tanstack Query Tailwind CSS</h1> </div> <div className= "overflow-x-auto pt-10" > <PostList postlists={data.postlist} /> <div className= "flex items-center justify-between my-5" > <Pagination totalPages={totalPages}/> </div> </div> </div> ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //my-app\src\pages\PostList.jsx const PostList = ({ postlists }) => { return ( <> <div className= "grid md:grid-cols-3 gap-5 mt-10" > {postlists.map((post) => ( <div key={post.id} className= "max-w-sm border border-gray-200 rounded-md shadow" > <div className= "relative aspect-video" > <img src={post._embedded[ "wp:featuredmedia" ][0].media_details.sizes.full.source_url} alt={post.title.rendered} sizes= "(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw" className= "rounded-t-md object-cover" /> </div> <div className= "p-5" > <h1> <a href={`/post/${post.slug}`}> {post.title.rendered} </a> </h1> </div> </div> ))} </div> </> ); }; export default PostList; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | //my-app\src\pages\Post.jsx import { useQuery } from "@tanstack/react-query" ; //npm i @tanstack/react-query import { useParams } from "react-router-dom" ; import { fetchPost } from "../api/post" ; const Read = () => { const { slug } = useParams(); console.log(slug); const { isLoading, isError, data: post, error, } = useQuery({ queryKey: [ "posts" , slug], queryFn: () => fetchPost(slug), }); console.log(post); if (isLoading) return "loading..." ; if (isError) return `Error: ${error.message}`; return ( <div className= "min-h-screen flex items-center justify-center bg-slate-100" > <div className= "bg-white rounded-sm shadow p-8 text-black" > { post.length ? ( <div> <h1 className= "text-2xl font-bold mb-5" >{post[0].title.rendered}</h1> <div className= "mb-4" > <div dangerouslySetInnerHTML={{ __html: post[0][ 'content' ][ 'rendered' ] }} /> </div> </div> ) : ( 'Loading....' ) } </div> </div> ) } export default Read |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //src\api\post.jsx const ITEM_PER_PAGE = 6; export async function getPostList(page) { const response = await fetch(`http: //localhost:8888/cairocoders/wp-json/wp/v2/posts?per_page=${ITEM_PER_PAGE}&page=${page}&_embed`); const totalData = response.headers.get( 'X-WP-Total' ); const data = await response.json(); console.log(data); return { postlist: data, totalpage: totalData, perpage: ITEM_PER_PAGE } } export async function fetchPost(slug) { const response = await fetch( "http://localhost:8888/cairocoders/wp-json/wp/v2/posts?_embed&slug=" +slug); return response.json() } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | //src\components\Pagination.jsx import { HiChevronLeft, HiChevronRight } from "react-icons/hi" ; //npm install react-icons --save npmjs com/package/react-icons import clsx from "clsx" ; //npm i clsx npmjs com/package/clsx import { useSearchParams, useLocation } from "react-router-dom" export const generatePagination = (currentPage, totalPages) => { if (totalPages <= 7) { return Array.from({ length: totalPages }, (_, i) => i + 1); } if (currentPage <= 3) { return [1, 2, 3, "..." , totalPages - 1, totalPages]; } if (currentPage >= totalPages - 2) { return [1, 2, 3, "..." , totalPages - 2, totalPages - 1, totalPages]; } return [ 1, "..." , currentPage - 1, currentPage, currentPage + 1, "..." , totalPages, ]; }; const Paginationnumber = ({ totalPages }) => { let location = useLocation(); console.log(location); const { hash, pathname, search } = location; //console.log(pathname); const [searchParams] = useSearchParams(); const currentPage = Number(searchParams.get( "page" )) || 1; //console.log(currentPage); 2 const allPages = generatePagination(currentPage, totalPages); const createPageURL = (pageNumber) => { const params = searchParams; params.set( "page" , pageNumber.toString()); return `${pathname}?${params.toString()}`; }; const PaginationNumber = ({ page, href, position, isActive, }) => { const className = clsx( "flex h-10 w-10 items-center justify-center text-sm border" , { "rounded-l-sm" : position === "first" || position === "single" , "rounded-r-sm" : position === "last" || position === "single" , "z-10 bg-blue-700 border-blue-500 text-white bg-blue-700" : isActive, "hover:bg-blue-700" : !isActive && position !== "middle" , "text-gray-300 pointer-events-none" : position === "middle" , } ); return isActive && position === "middle" ? ( <div className={className}>{page}</div> ) : ( <a href={href} className={className}> {page} </a> ); }; const PaginationArrow = ({ href, direction, isDisabled }) => { const className = clsx( "flex h-10 w-10 items-center justify-center text-sm border" , { "pointer-events-none text-blue-300" : isDisabled, "hover:bg-blue-700" : !isDisabled, "mr-2" : direction === "left" , "ml-2" : direction === "right" , } ); const icon = direction === "left" ? ( <HiChevronLeft size={20} /> ) : ( <HiChevronRight size={20} /> ); return isDisabled ? ( <div className={className}>{icon}</div> ) : ( <a href={href} className={className}> {icon} </a> ); }; return ( <div className= "inline-flex" > <PaginationArrow direction= "left" href={createPageURL(currentPage - 1)} isDisabled={currentPage <= 1} /> <div className= "flex -space-x-px" > {allPages.map((page, index) => { let position; if (index === 0) position = "first" ; if (index === allPages.length - 1) position = "last" ; if (allPages.length === 1) position = "single" ; if (page === "..." ) position = "middle" ; return ( <PaginationNumber key={index} href={createPageURL(page)} page={page} position={position} isActive={currentPage === page} /> ); })} </div> <PaginationArrow direction= "right" href={createPageURL(currentPage + 1)} isDisabled={currentPage >= totalPages} /> </div> ); }; export default Paginationnumber; |
C:\react-js\my-app> npm run dev
themes\cairocoders\functions.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //themes\cairocoders\functions.php <?php /** * Theme functions and definitions * * @package cairocoders */ add_action( 'rest_api_init' , 'register_rest_images' ); function register_rest_images(){ register_rest_field( array ( 'post' ), 'fimg_url' , array ( 'get_callback' => 'get_rest_featured_image' , 'update_callback' => null, 'schema' => null, ) ); } function get_rest_featured_image( $object , $field_name , $request ) { if ( $object [ 'featured_media' ] ){ $img = wp_get_attachment_image_src( $object [ 'featured_media' ], 'app-thumb' ); return $img [0]; } return false; } |