You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现筛选结果自动更新博客文章列表?

博客搜索+无限滚动问题的解决方案

问题根源

提交搜索表单后,URL的筛选参数filter已更新,但组件没有自动触发新的搜索流程——现有帖子列表保留着旧数据,分页、加载状态也未重置,必须手动刷新才能加载符合新条件的内容。


具体解决步骤

1. 监听筛选参数变化,重置状态并加载初始数据

添加一个useEffect,当filter(从URL的searchParams获取)变化时,重置所有和数据、分页相关的状态,并发起第一页的搜索请求:

useEffect(() => {
  // 重置状态:清空现有帖子、回到第0页、恢复加载更多的开关
  setPosts([]);
  setPage(0);
  setMorePosts(true);
  // 请求新筛选条件下的第一页数据
  fetchPosts(`http://localhost:4000/blog?skip=0&limit=1&q=${filter || ''}`);
}, [filter]); // 仅当filter变化时触发

2. 优化搜索提交逻辑(可选)

原代码中submissionHandler里的e.target.reset()属于重复操作(已经通过setSearchField('')清空了输入框),可以去掉:

const submissionHandler = (e) => {
  e.preventDefault();
  setSearchParams({ filter: searchField });
  setSearchField('');
};

3. 精简Intersection Observer的依赖项

原useEffect依赖了posts,会导致每次帖子更新都重建观察者,实际上只需要依赖filter和morePosts即可,避免不必要的性能消耗:

useEffect(()=>{
    const observer = new IntersectionObserver(onIntersect)
    if (elementRef.current){
        observer.observe(elementRef.current)
    }
    return ()=>{
        observer.disconnect()
    }
},[filter, morePosts])

修改后的完整组件代码

export default function Posts (){
    const {title} = useParams()
    const [searchParams,setSearchParams] = useSearchParams()
    const [posts,setPosts] = useState([])
    const [morePosts ,setMorePosts] =useState(true)
    const [page,setPage ]= useState(0)
    const elementRef = useRef(null)
    const filter = searchParams.get('filter')
    const [searchField,setSearchField] = useState('')

    const onSearchChange=(e)=>{
        setSearchField(e.target.value)
    }

    const submissionHandler =(e)=>{
        e.preventDefault()
        setSearchParams({filter:searchField})
        setSearchField('')
    }

    const onIntersect=(entries)=>{
        if(entries[0].isIntersecting && morePosts ){
            fetchPosts(`http://localhost:4000/blog?skip=${page}&limit=1&q=${filter?filter:''}`)
        }
    }

    function fetchPosts(url){
      fetch(url,{
                method:'get',
                mode:'cors',
            })
            .then(response=>response.json())
            .then(data=>{
                if (data.posts.length === 0){
                    setMorePosts(false)
                }
                else if(data.success){
                    setPosts(p=>[...p,...data.posts])
                    setPage(prev=>prev+1)
                }
            })
    }

    // 新增:监听filter变化,重置状态并加载初始数据
    useEffect(() => {
      setPosts([]);
      setPage(0);
      setMorePosts(true);
      fetchPosts(`http://localhost:4000/blog?skip=0&limit=1&q=${filter || ''}`);
    }, [filter]);

    // 优化:调整观察者依赖项
    useEffect(()=>{
        const observer = new IntersectionObserver(onIntersect)
        if (elementRef.current){
            observer.observe(elementRef.current)
        }
        return ()=>{
            observer.disconnect()
        }
    },[filter, morePosts])

    return(
        <>
            <FormComponent 
                style={{width:'20rem',alignSelf:'center',marginBottom:'10pt'}}
                onSubmit={submissionHandler}
            >
                <input type='text' placeholder='Search Posts' value={searchField} onChange={e=>onSearchChange(e)}/>
            </FormComponent>
            {posts.map((post,i)=>{
                const publishedDate = new Date(post.published_at)
                const options = { year: 'numeric', month: 'short', day: 'numeric' }
                const postDate = publishedDate.toLocaleDateString('en-us',options)
                return (
                    <BlogCard 
                        key={i+post.title}
                        id={i}
                        cardImage={post.post_img}
                        slug={post.slug}
                        title={post.title}
                        author={post.author}
                        body={post.body}
                        date={postDate}
                        tags={
                            post.tags.map((tag,i)=>{
                                return <span 
                                    style={{cursor:'pointer'}} key={i} >
                                    {tag}
                                </span>
                            })
                        }
                        categories={
                            post.categories.map((cat,i)=>{
                                return <span 
                                    style={{cursor:'pointer'}} key={i} >
                                    {cat}
                                </span>
                            })
                        }
                    />
                )
            })}
             {morePosts && <div ref={elementRef}>...Load More Posts</div>}
        </>
    )
}

内容的提问来源于stack exchange,提问作者Nineisten

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 16:47:08