質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

689閲覧

React Hooksで複数のイベント処理を管理したいです

Umesh

総合スコア1

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2022/11/19 17:07

編集2022/11/20 02:22

前提

ここに質問の内容を詳しく書いてください。
React Hooksでブログを作ろうとしています。
データベースからブログのリストを取得する関数があるのですが、手を加えて「検索ワード」「タグ」をそれぞれ使いブログのリストをフィルタリングしようとしています。

リストの中にはデータベースから取得した、タイトル、本文、タグなどの情報が含まれており、検索ワードをinputタグに入力したらonChangeイベントでタイトルまたは本文に該当するワードが含まれる記事をフィルタリングできました。

ですが、同時にタグが書かれたAタグを押すとonChangeイベントが何度か押さないと反応されません。

実現したいこと

最終的なゴールは、ブログリストのデータを様々な条件でフィルタリングしたいです。
ですが複数のフィルタリング条件を複数のonChangeイベントで扱う時にどのように書くのが最適解なのかわかりません。

エラーの内容

ブラウザのConsoleではエラーはでなかったのですがonChangeイベントが不発して動かない時があります。

該当のソースコード

今は手元に開発環境がなくスマホから質問を投稿させていただいておりますため、まずは上記の情報だけでワークアラウンドもしくは、例的な複数イベント処理の書き方が思い当たる方がいらっしゃれば回答いただきたいです。

react.js

試したこと

検索ワードを保持するuseState
押されたタグを保持するuseState

2つのuseStateがあり、それぞれのonChangeイベントでフィルタリングされる文字がsetされるようになっています。

ブログリストを表示する関数の中で、検索ワードまたはタグが入力されたらor条件でフィルタリングするように書きました。

補足情報(FW/ツールのバージョンなど)

必要があればライブラリ等のバージョンも調べます。よろしくお願いします。

コード抜粋(追記)

<Child.js(一部抜粋)>

const getTagMenu = props.tags.map((data, index) => { return ( <React.Fragment key={index}> <li className="list-inline-item"> <Link to="#" onClick={e => props.setTag(e.target.textContent)} className="badge bg-light font-size-12 mt-2"> {data} </Link> </li> </React.Fragment> ) }); return ( <React.Fragment> <input type="text" id="search" className="form-control rounded bg-light border-light" placeholder="キーワードを入力" onChange={e => props.setKeyword(e.target.value)} /> <div> <p className="text-muted mb-1"><strong>タグ</strong></p> <ul className="list-inline widget-tag"> {getTagMenu} </ul> </div> </React.Fragment> )

<Parent.js(一部抜粋)>

const [keyword, setKeyword] = useState(""); const [tag, setTag] = useState(""); const filterSearchWord = (blog) => { return (blog.text.toLowerCase().indexOf(keyword.toLowerCase()) > -1) || (blog.title.toLowerCase().indexOf(keyword.toLowerCase()) > -1); }; const filterTag = (blog) => { return (blog.tags.indexOf(tag) > -1); }; const filterMulti = (blog) => { return filterTag(blog) || filterSearchWord(blog); }; const getBlog = (data) => { data = data.filter( (blog) => { return filterMulti(blog); }); data.sort(); const prefix = '../../'; return data.map((blog, index) => { return ( <React.Fragment key={index}> <div> <h5> <Link to="blog-details" className="text-dark"> {blog.title} </Link> </h5> <p className="text-muted">{blog.shortInsertTime}</p> <div className="position-relative mb-3"> <img src={prefix + blog.image} alt="" className="img-thumbnail" /> </div> <ul className="list-inline"> <li className="list-inline-item mr-3"> <Link to="#" className="text-muted"> <i className="bx bx-comment-dots align-middle text-muted me-1"></i>{" "} {blog.comment} Comments </Link> </li> {getTags((blog.tags).split("&"))} </ul> <p> {blog.text} </p> <div> <Link to="#" className="text-primary"> Read more <i className="mdi mdi-arrow-right"></i> </Link> </div> </div> <hr className="my-5" /> </React.Fragment> ) }) } return ( <React.Fragment> <FrontMenu /> <div className="page-content"> <MetaTags> <title>タイトル(仮)</title> </MetaTags> <Container fluid> <Row> <BlogList blog = {getBlog({activitylog}.activitylog)} btnprimary = {btnprimary} page = {page} step = {step} activeTab = {activeTab} setPage = {setPage} setStep = {setStep} setBtnprimary = {setBtnprimary} /> <RightBar setKeyword = {setKeyword} setTag = {setTag} tags = {getAllTags} /> </Row> </Container> </div> </React.Fragment> )

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

コードを見ないとはっきりしたことは言えませんが、質問を一読したかぎりでは

タグが書かれたAタグ

の onChange ではなく onClick に適切なハンドラーが設定されているかどうかを見直すとよいのでは?と思いました。

参考

以下はchange イベントが発行されるHTML要素についてのMDNの説明です。

HTMLElement: change イベント

change イベントは <input>, <select>, <textarea> 要素において、ユーザーによる要素の値の変更が確定したときに発行されます。

追記

質問に追加されたソースコードを拝見して、おそらく原因(のひとつ)は Parent コンポーネントのstate tag を更新する setTag が呼ばれるときの引数の文字列の前後に余計な空白文字(スペースや改行)があることではないかと推測します。

これを確認する方法としては、useState で得た setTag をそのまま <RightBar />のprop に渡すのではなく、

javascript

1 const handleChangeTag = useCallback( 2 (text) => { 3 console.log(`<${text}>`); // 空白文字の存在を確認しやすくするため< と > で囲む。 4 setTag(text); 5 }, 6 [setTag] 7 );

という関数を作っておきます。(このような、子コンポーネントのpropに渡すコールバック関数の名前の先頭を handleにするのが慣習となっていることがあります。)
追加する場所はとりあえず、const [tag, setTag] = useState(""); の次の行あたりが分かりやすいかと思います。そして、これを <RightBar /> の prop setTag に渡すようにします。

diff

1<RightBar 2・・・ 3- setTag = {setTag} 4+ setTag = {handleChangeTag}

こうすると、(useCallbackによってメモ化された) handleChangeTag関数の中で setTag(text); が実行される直前で

javascript

1console.log(`<${text}>`);

が実行されますが、このときのログが <とタグの間あるいはタグと> の間に空白や改行があって、たとえば タグが 社会であるとして

"< 社会 >"

といったようなものになっているならば、このような前後に空白文字を含むまま setTag に渡されて tag として更新されるので、 filter の条件である

javascript

1blog.tags.indexOf(tag) > -1

は(blog.tagsに含まれる各タグ文字列の前後には空白文字は無いでしょうから)true になることがないです。

もし上記の確認によって、実際に前後に空白文字が含まれていた場合は何らかの対策をして、前後の余分な空白文字が無い文字列が setTag に渡されるように修正する必要があります。

投稿2022/11/19 19:08

編集2022/11/20 04:37
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Umesh

2022/11/20 02:23 編集

su507様 回答ありがとうございます。 失礼しました。タグのほうはonClickのイベントハンドラーが設定されていました。 帰宅し、パソコンからソースコードを抜粋させていただきました。最初の投稿の末尾に追記させていただいてます。 諸事情からすべてのコードを記載することができないのですが、必要な情報は全て記載していきます。
退会済みユーザー

退会済みユーザー

2022/11/20 03:31

ソースコードの提示ありがとうございます。原因と思われる点と対策を追記しました。
Umesh

2022/11/20 04:39

su507様 引き続き、回答ありがとうございます。 いただいたコードを埋め込み検証しました。入力されたフィルタ条件が可視化されやすかったです。 ふが というタグを押したところConsoleに <ふが> と表示されており前後の空白や改行は問題なさそうでした。 引き続き動作を確認していて挙動がきになったのが、 検索ワードを入力しても反応なく、タグを押すとフィルタされる 逆に、 タグだけ押しても反応がなく、検索ワードを入力されるとフィルタされるという2つの項目がないとイベントハンドラーが挙動しませんでした。 下記のように||でor条件にしているのですが片方の関数を削ると残したフィルタだけchangeイベントやclickイベントに反応してくれます。 両方をorで条件にするとどちらもイベントを起こさないと反応してくれない というような挙動をしているようです...。 ////// const filterMulti = (blog) => { return filterTag(blog) || filterSearchWord(blog); };
退会済みユーザー

退会済みユーザー

2022/11/20 04:59 編集

> ふが というタグを押したところConsoleに <ふが> と表示されており前後の空白や改行は問題なさそうでした。 確認ありがとうございます。了解しました。 次に、 > 引き続き動作を確認していて挙動がきになったのが、 との挙動の説明を読んで、もう一つ思いあたりました。文字列の indexOf メソッドの返す値についてです。 任意の文字列、たとえば 'abc' に対して、 'abc'.indexOf('z') は -1 を返します。 'abc'.indexOf('a') は 0 を返します。 混乱の元になるのが indexOf の引数に空文字列を渡した 'abc'.indexOf('') も 0 を返すという点です。 この点がもし現状では考慮されていなければ、 条件による抽出のロジックに何らかの追加、修正が必要になると思います。
退会済みユーザー

退会済みユーザー

2022/11/20 05:12

上記コメントに書いた > ndexOf の引数に空文字列を渡した > 'abc'.indexOf('') > も 0 を返すという点 については、意図通り動かない原因ではないかもしれませんね。 ただ > 'abc'.indexOf('') > も 0 を返すという点 を考慮していなかったならば、確認すべき観点の一つかなとは思います。
Umesh

2022/11/20 05:20

su507様 ありがとうございます。用事があり1度パソコンを離れてしまうため、夜戻りましたらご指摘の箇所を再度確認してみます。 indexOfの細かな仕様を理解しておらず、仰る通り空文字などの考慮漏れの可能性が高いです。
Umesh

2022/11/20 14:41 編集

戻ってきたので動作確認を行いましたが、動作確認が不十分のまま回答してしまったため一旦回答を編集させていただきます。
Umesh

2022/11/20 15:27

su507様 ありがとうございました。おかげさまで複数のフィルタに同時対応できるようになりました。 ご指摘の通り空文字などを考慮してなかったが故に、想定と異なる結果を返しておりました。 最終的に下記の通りに関数を修正した所、想定通りの動作を確認できました。 const filterSearchWord = (blog) => { return !(keyword === '' && typeof keyword === 'undefined') ? (blog.text.toLowerCase().indexOf(keyword.toLowerCase()) > -1) || (blog.title.toLowerCase().indexOf(keyword.toLowerCase()) > -1) : false; }; const filterTag = (blog) => { return !(tag === '' && typeof tag === 'undefined') ? (blog.tags.indexOf(tag) > -1) : false; }; const filterMulti = (blog) => { return (filterTag(blog) && filterSearchWord(blog)); };
退会済みユーザー

退会済みユーザー

2022/11/20 21:27

解決されたようですね、よかったです 👏
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問