如何在JavaScript/TypeScript中检查字符串是否包含子串?React搜索功能模糊匹配优化求助
Let's get your search functionality working the way you want it—you're already halfway there, just a couple of missteps in how you're applying the filtering logic. Here's what's going wrong and how to fix it:
What's Causing the Issue
- You're not using your filtered results: You've already written a
searchfunction that correctly filters posts into theresultstate, but your rendering code is still looping through the originalpostListinstead of using the filteredresultarray. That's why yourincludesattempts weren't having the intended effect. - Reverse logic in conditional checks: Your original condition
if (query !== post.title && query) return nullenforces exact matches only. When you triedincludes, you flipped the check (e.g.,query.includes(post.title)instead ofpost.title.includes(query)), which checks if your search term contains the entire post title—opposite of what you need.
Step-by-Step Solution
1. Update Render Logic to Use Filtered Results
Modify your rendering section to use result when a search query exists, and fall back to postList when there's no query. This way, you're actually displaying the filtered posts you generate in the search function.
2. Remove the Broken Conditional in map
Since we're already filtering the posts before rendering, we don't need to return null for non-matching posts inside the map—just render every post in the active array (either result or postList).
3. (Optional) Add Real-Time Search
Instead of requiring a button click to search, you can trigger filtering automatically as the user types by moving the search logic into a useEffect that listens for changes to query.
Modified Code
Here's your full component with the fixes applied:
import React, { useState, useEffect, useCallback } from 'react' import api from 'services/api' import { Link } from 'react-router-dom' import { RectShape } from 'react-placeholder/lib/placeholders' import { AiFillCalendar } from 'react-icons/ai' import { FaUserCircle } from 'react-icons/fa' import NoDataWarning from 'components/NoDataWarning' import Can from 'components/Can' import PayPlaceholder from 'components/PayPlaceholder' import { Post } from 'types' import { ButtonIcon, SearchGroup, SearchInput } from 'components/SearchBar/styles' import { BiSearchAlt2 } from 'react-icons/bi' import { Container, PostContainer, PostTitle, PostBanner, PostInfoContainer, PostGrid } from './styles' const RegulatorioList: React.FC = () => { const [postList, setPostList] = useState<Post[]>([]) const [isLoading, setIsLoading] = useState(true) const [query, setQuery] = useState('') const [result, setResult] = useState<Post[] | undefined>() const inputHandler = (event: React.ChangeEvent<HTMLInputElement>) => { const enteredName = event.target.value setQuery(enteredName) } // This function filters posts based on the query const filterPosts = useCallback(() => { if (!query.trim()) { setResult(undefined) // Reset to show all posts when query is empty return } const foundItems = postList.filter(post => post.title.toLowerCase().includes(query.toLowerCase()) ) setResult(foundItems) }, [postList, query]) // Trigger filtering on query change (real-time search) useEffect(() => { filterPosts() }, [filterPosts]) const fetchPosts = useCallback(() => { api .get<Post[]>('posts', { params: { filterInactives: true } }) .then(response => { setPostList(response.data) setIsLoading(false) }) }, []) useEffect(() => { fetchPosts() const interval = setInterval(fetchPosts, 180000) return () => clearInterval(interval) }, [fetchPosts]) const LoadingBlock = () => ( <> {new Array(15).fill('').map(() => ( <RectShape color='lightgray' style={{ width: '100%', height: '350px', borderRadius: '5px' }} className='text-block' /> ))} </> ) // Determine which posts to render const postsToRender = query.trim() ? result : postList return ( <Container> <SearchGroup position={1}> <SearchInput value={query} onChange={inputHandler} placeholder='Search ' /> {/* Optional: Keep the button if you want both click and real-time search */} <ButtonIcon type='button' onClick={filterPosts}> <BiSearchAlt2 /> </ButtonIcon> </SearchGroup> <PostGrid> {(() => { if (isLoading) return <LoadingBlock /> if (!isLoading && (!postsToRender || postsToRender.length === 0)) { return <NoDataWarning text='Nenhuma postagem encontrada.' /> } return postsToRender.map((post, index) => ( <Link key={post.id} to={`/regulatorio/${post.id}`} style={{ textDecoration: 'none', height: '100%' }}> <PostContainer position={index}> <PostBanner /> <PostInfoContainer> <span> <AiFillCalendar /> {new Date(post.createdAt).toLocaleDateString()} </span> <span> <FaUserCircle /> {`${post.firstName} ${post.lastName}`} </span> </PostInfoContainer> <PostTitle>{post.title}</PostTitle> </PostContainer> </Link> )) })()} </PostGrid> </Container> ) } export default RegulatorioList
Key Changes Explained
filterPostsfunction: Refactored the search logic into a reusable function that checks if the post title includes the query (case-insensitive for better user experience).- Real-time search: Added a
useEffectthat callsfilterPostswheneverquerychanges, so results update as the user types. postsToRendervariable: Decides whether to show filtered results or all posts based on the presence of a query.- Removed broken conditionals: The
mapnow just renders every post in the active array, no more returningnullfor non-matching posts. - Fixed empty state check: Now checks
postsToRenderinstead ofpostListto show the "no data" message when search returns no results.
内容的提问来源于stack exchange,提问作者Lucas Abrantes

