当サイトは、アフィリエイト広告を利用しています
everyメソッドを使ってReact(typescript)のタグ絞り込み機能を
サンプル実装してみる
everyは配列の各要素に対して、関数を実行し、 boolean値を返却する。
// 配列を定義const array:Array<number> = Array.of(1,2,3,4,5)// everyの関数の結果が全てtrueの場合にtrueが返却されるconst result1:boolean = array.every(val=>val>0)console.log(result1)// everyの関数の結果が一つでもfalseの場合にfalseが返却されるconst result2:boolean = array.every(val=>val>2)console.log(result2)// 実行結果// true// false
サンプルとしてタグの配列を持つ記事オブジェクトリストから
選択したタグを全て含む記事を取得する実装をしてみる
// 選択済タグconst selectedTag = Array.of("javascript", "react");// 記事一覧const articleList = Array.of({ title: "記事1", tags: ["java"], cotent: "aaaaaaaaaa" },{title: "記事2",tags: ["react", "javascript"],cotent: "bbb"},{ title: "記事3", tags: ["emotion"], cotent: "cccc" },{ title: "記事4", tags: ["javascript"], cotent: "ddddddddddddd" },{ title: "記事5", tags: ["css"], cotent: "eeeeeeeeeee" },{ title: "記事6", tags: ["gatsby"], cotent: "f" });// 選択済タグを全て含む記事を抽出const filterdList = articleList.filter((article) => {// 選択済タグをeveryでループ// everyに渡した関数が全てtrueの場合にtrueが返されるreturn selectedTag.every((tag) => {// findで記事のタグをループして探す// 一致する場合はその値が返却されるconst findResult = article.tags.find((articleTag) => articleTag === tag);// 選択済タグが記事のタグになかった場合はfalseを返すif (findResult) {return true;} else {return false;}});});console.log(JSON.stringify(filterdList, null, 3));// 実行結果// 0: {// title: "記事2"// tags: {// 0: "react"// 1: "javascript"// }// cotent: "bbb"// }
filter,every,findを使用して実現している
上記のタグで記事を絞り込む機能を
reactで実装してみる
/** @jsxImportSource @emotion/react */import { css } from "@emotion/react";import { useEffect, useState } from "react";import { tagsGrid, articleGrid } from "./emotionCss";type article = {title: string;tags: string[];cotent: string;};export default function App() {// タグ初期値const array: Array<string> = Array.of("java","javascript","gatsby","react","css","emotion");// 記事初期値const list: Array<article> = Array.of({ title: "記事1", tags: ["java"], cotent: "aaaa" },{ title: "記事2", tags: ["react", "javascript"], cotent: "bbbbbb" },{ title: "記事3", tags: ["emotion"], cotent: "cccc" },{ title: "記事4", tags: ["javascript"], cotent: "ddd" },{ title: "記事5", tags: ["css"], cotent: "ee" },{ title: "記事6", tags: ["gatsby"], cotent: "f" });// 選択中タグのstateconst [selectedTag, selectTag] = useState<Array<string>>([]);// タグ検索結果のstate// 初期値は全記事const [searchResult, doSearch] = useState<Array<article>>(list);//選択されたタグで記事を抽出する// useStateと同じイベントで書くと同じイベント内ではstateが更新されないためうまく行かない// そのため抽出処理はuseEffectで書くuseEffect(() => {// 検索結果記事を抽出するdoSearch((prevList) => {// タグに一致する記事をfilterで抽出let newList = prevList.filter((obj) => {// everyを使って選択中のタグを全て持っている// 記事を抽出// ※everyは実行する関数の結果が全てtrueの場合のみtrueが返されるlet result = selectedTag.every((tag) => {// 選択中タグが記事のタグ内にあるかfindで探す// findの結果はあればその値が返却されるconst findResult = obj.tags.find((kijiTag) => kijiTag === tag);// find結果が存在する場合はtrueで返却// ここが全てtrueの時にresultがtrueになるif (findResult) {return true;} else {return false;}});// 選択中タグを全て含む記事をtrueとして返却return result;});return newList;});console.log("start");// クリーンアップ処理return () => {// 検索結果を初期状態に戻すdoSearch(() => list);console.log("clean");};// タグが選択された時に実行する}, [selectedTag]);// タグ選択処理const push = (e: any) => {e.preventDefault();// タグが既に選択済かチェックするconst check = selectedTag.find((tag) => {return tag === e.target.id;});if (!check) {//未選択の場合は追加selectTag((selected) => [...selected, e.target.id]);} else {// 選択済の場合は削除// filterで選択したタグ以外のlistを作り直すselectTag((selected) => selected.filter((tag) => tag !== e.target.id));}};// タグ選択時のスタイル変更const grigItem = (selectTag: string[], select: string) => {// 選択したタグが既に選択済かチェックconst count = selectTag.filter((selected) => select === selected);// 選択してない場合はスタイル追加if (count.length !== 0) {return [css`background-color: red;`];}};return (<div className="App"><h1>Hello CodeSandbox</h1><h2>タグ</h2><div css={[tagsGrid]}>{array.map((val, i) => (<buttonkey={i}id={val}css={[grigItem(selectedTag, val)]}onClick={(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) =>push(e)}>{val}</button>))}</div><h2>記事</h2><div css={[articleGrid]}>{searchResult.map((kiji, i) => (<div key={i}><h2>{kiji.title}</h2><divcss={[css`height: 50px;`]}>{kiji.tags.map((tag) => (<div>{tag}</div>))}</div><h3>{kiji.cotent}</h3></div>))}</div></div>);}
スタイリングはEmotionでする
/** @jsxImportSource @emotion/react */import { css } from "@emotion/react";export const tagsGrid = () => [css`display: grid;grid-template-columns: repeat(3, minmax(100px, 10%));grid-auto-rows: 50px;justify-content: center;align-items: center;gap: 10px;border: 1px solid black;margin: auto;`];export const articleGrid = () => [css`display: grid;grid-template-columns: repeat(4, minmax(100px, 10%));justify-content: center;align-items: start;gap: 20px;border: 1px solid black;margin: auto;`];
大分複雑になってしまったが実装することができた。
要点としてはReactでは同一レンダー内ではuseStateは更新されないので
タグの選択はuseStateのset関数で実行し、絞り込みはuseEffectで実行すること