こんにちは、KOUKIです。
Reactで画面開発をハンズオン形式で紹介しています。
今回は、ソート機能の実装を行いましょう。
尚、「React, NextJS and Golang: A Rapid Guide – Advanced」コースを参考にしています。解釈は私が勝手に付けているので、本物をみたい場合は受講をお勧めします!
<目次>
前回
前回は、検索機能を実装しました。
ソート機能
以前、RedisでSort機能の実装を行いました。
ここでは、URLの末尾に「sort」と「asc/desc」キーワードを指定して、Productの価格順に並べ替えを行いました。
1 2 3 |
// 例 http://localhost:8000/api/ambassador/products/backend?sort=asc http://localhost:8000/api/ambassador/products/backend?sort=desc |
UI側では、この機能を利用して、商品のソート処理を実装しましょう。
プルダウンの設置
ソートの切り替えは、プルダウンで行います。
以前実装したProducts.tsxに実装しましょう。
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 |
// pages/Products.tsx ... const Products = (props: { ... return ( <> <div className="col-md-12 mb-4 input-group"> <input type="text" className="form-control" placeholder="Search" onChange={e => search(e.target.value)} /> // 追加 <div className="input-group-append"> <select className="form-select"> <option>Select</option> <option value="asc">Price Ascending</option> <option value="desc">Price Descending</option> </select> </div> </div> ... } |

Filtersモデルの修正
前回作成したFiltersモデルに、sortを追加します。
1 2 3 4 5 |
// models/filters.ts export interface Filters { q: string sort: string } |
Filtersモデルの修正に伴い、この型で定義付けた処理の修正を行います。
1 2 3 4 5 6 7 8 |
// pages/ProductsFrontend.ts const ProductsFrontend = () => { ... const [filters, setFilters] = useState<Filters>({ q: '', sort: '', // 追加 }) } |
1 2 3 4 5 6 7 8 9 10 |
// pages/ProductsBackend.tsx ... const ProductsBackend = () => { ... const [filters, setFilters] = useState<Filters>({ q: '', sort: '',// 追加 }) } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// pages/Products.tsx ... const Products = (props: { products: Product[] filters: Filters, setFilters: (filters: Filters) => void }) => { const search = (q: string) => { props.setFilters({ ...props.filters, // 修正 q }) } } |
search関数のsetFiltersでは「q」と指定していますが、これはFiltersのパラメータのkeyである「q」と合わせているので、いい感じに展開してくれます。そのためには、「…props.filters」のように全ての値を展開する処理を先に書く必要があります。
sort関数の実装
sort関数を実装しましょう。
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 |
// pages/Products.tsx ... const Products = (props: { products: Product[] filters: Filters, setFilters: (filters: Filters) => void }) => { ... const sort = (sort: string) => { props.setFilters({ ...props.filters, sort }) } return ( <> <div className="col-md-12 mb-4 input-group"> ... <div className="input-group-append"> <select className="form-select" onChange={e => sort(e.target.value)}> ... </select> </div> </div> ... </> ) } export default Products |
sort関数を実行するために、selectタグにonChangeを指定しました。
ProductsFrontend/BackendのuseEffectの処理を修正しましょう。
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 |
// pages/ProductsFrontend.tsx ... const ProductsFrontend = () => { ... useEffect(() => { let products = allProducts.filter( p => p.title.toLocaleLowerCase().indexOf( filters.q.toLowerCase()) >= 0 || p.description.toLowerCase().indexOf( filters.q.toLowerCase()) >= 0) if (filters.sort === 'asc') { products.sort((a: Product, b: Product) => { if (a.price > b.price) { return 1 } if (a.price < b.price) { return -1 } return 0 }) } else if (filters.sort === 'desc') { products.sort((a: Product, b: Product) => { if (a.price > b.price) { return -1 } if (a.price < b.price) { return 1 } return 0 }) } setFilterProducts(products) }, [filters]) ... } export default ProductsFrontend |
FrontEndは、APIへリクエストを飛ばさないので、JavaScriptのsort機能を使って実現しました。
一方、Backend側では、sortキーをURL末尾に付与してからAPIへリクエストを送り、ソートされたデータを受け取ります。
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 |
// pages/ProductsBackend.tsx ... const ProductsBackend = () => { ... useEffect(() => { ( async () => { const arr = [] if (filters.q) { arr.push(`q=${filters.q}`) } if (filters.sort) { // 追加 arr.push(`sort=${filters.sort}`) } const {data} = await axios.get( backendUrl + '?' + arr.join('&')) if (data.data) { setProducts(data.data) } } )() }, [filters]) } |
検証
次回
Reactまとめ
参考書籍
ソースコード
Products.tsx
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 |
// pages/Products.tsx import { Product } from '../models/product' import { Filters } from '../models/filters' const Products = (props: { products: Product[] filters: Filters, setFilters: (filters: Filters) => void }) => { const search = (q: string) => { props.setFilters({ ...props.filters, q }) } const sort = (sort: string) => { props.setFilters({ ...props.filters, sort }) } return ( <> <div className="col-md-12 mb-4 input-group"> <input type="text" className="form-control" placeholder="Search" onChange={e => search(e.target.value)} /> <div className="input-group-append"> <select className="form-select" onChange={e => sort(e.target.value)}> <option>Select</option> <option value="asc">Price Ascending</option> <option value="desc">Price Descending</option> </select> </div> </div> <div className="row row-cols-1 row-cols-sm-2 row-cols-md-3 g-3"> {props.products.map(product => { return ( <div className="col" key={product.id}> <div className="card shadow-sm"> <img src={product.image} height={200} /> <div className="card-body"> <p className="card-text"> {product.title} </p> <div className="d-flex justify-content-between align-items-center"> <small className="text-muted">${product.price}</small> </div> </div> </div> </div> ) })} </div> </> ) } export default Products |
ProductsFrontend.tsx
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 |
// pages/ProductsFrontend.tsx import { useEffect, useState } from 'react' import { Product } from '../models/product' import { Filters } from '../models/filters' import Layout from '../components/Layout' import Products from '../pages/Products' import axios from 'axios' const ProductsFrontend = () => { const frontendUrl = 'products/frontend' const [filterProducts, setFilterProducts] = useState<Product[]>([]) const [allProducts, setAllProducts] = useState<Product[]>([]) const [filters, setFilters] = useState<Filters>({ q: '', sort: '', }) useEffect(() => { ( async () => { const {data} = await axios.get(frontendUrl) if (data) { setAllProducts(data) setFilterProducts(data) } } )() }, []) useEffect(() => { let products = allProducts.filter( p => p.title.toLocaleLowerCase().indexOf( filters.q.toLowerCase()) >= 0 || p.description.toLowerCase().indexOf( filters.q.toLowerCase()) >= 0) if (filters.sort === 'asc') { products.sort((a: Product, b: Product) => { if (a.price > b.price) { return 1 } if (a.price < b.price) { return -1 } return 0 }) } else if (filters.sort === 'desc') { products.sort((a: Product, b: Product) => { if (a.price > b.price) { return -1 } if (a.price < b.price) { return 1 } return 0 }) } setFilterProducts(products) }, [filters]) return ( <Layout> <Products products={filterProducts} filters={filters} setFilters={setFilters} /> </Layout> ) } export default ProductsFrontend |
ProductsBackend.tsx
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 |
// pages/ProductsBackend.tsx import { useEffect, useState } from 'react' import { Product } from '../models/product' import { Filters } from '../models/filters' import Layout from '../components/Layout' import Products from '../pages/Products' import axios from 'axios' const ProductsBackend = () => { const backendUrl = 'products/backend' const [products, setProducts] = useState<Product[]>([]) const [filters, setFilters] = useState<Filters>({ q: '', sort: '', }) useEffect(() => { ( async () => { const arr = [] if (filters.q) { arr.push(`q=${filters.q}`) } if (filters.sort) { arr.push(`sort=${filters.sort}`) } const {data} = await axios.get( backendUrl + '?' + arr.join('&')) if (data.data) { setProducts(data.data) } } )() }, [filters]) return ( <Layout> <Products products={products} filters={filters} setFilters={setFilters} /> </Layout> ) } export default ProductsBackend |
filters.ts
1 2 3 4 5 |
// models/filters.ts export interface Filters { q: string sort: string } |
コメントを残す
コメントを投稿するにはログインしてください。