You need to enable JavaScript to run this app. React配合react-transition-group搜索跳转后组件重复渲染旧props问题
优惠活动
大模型
产品
解决方案
定价
更多

React配合react-transition-group搜索跳转后组件重复渲染旧props问题

问题说明

技术栈

  • React 17.0.2
  • react-router-dom 5.2.0
  • react-transition-group 4.4.2

实现代码

头部搜索组件

const history = useHistory(); 
const [searchBarText, setsearchBarText] = useState(""); 

return(
<NavItem>
  <div>
    <input 
      type="text" 
      name="search" 
      value={searchBarText} 
      onChange={(e)=> setsearchBarText(e.target.value)} 
      onKeyPress={(e) => { 
        if (e.key === "Enter") { 
          history.push("/search?q=" + searchBarText); 
          console.log( "...pushed to history: ", searchBarText, " history is : ", history ); 
          setsearchBarText(""); 
        } 
      }} 
    />
    <NavLink to={ "/search?q=" + searchBarText} onClick={()=> setsearchBarText("")} >
      <i className="fas fa-search"></i>
    </NavLink>
  </div>
</NavItem>
);

路由配置

const location = useLocation(); 
const SearchWithQuery = ({ location }) => (
  <Search queries={getQueryObjects(location.search)} /> 
); 
return (
  <>
    <Header />
    <TransitionGroup>
      <CSSTransition key={location.key} classNames="slide" timeout={2000}>
        <Switch location={location}>
          {/* 其他路由 */}
          <Route path="/search" component={ SearchWithQuery} />
        </Switch>
      </CSSTransition>
    </TransitionGroup>
  </>
);

入口文件

import { BrowserRouter} from "react-router-dom";
<BrowserRouter>
  <App />
</BrowserRouter>

Search组件

const Search = ({ queries }) => {
  console.log("...from search component: queries: ", queries);
  return <></>;
};

export default Search;

问题表现

预期行为

输入搜索内容后点击搜索图标或按回车键,调用history.push跳转至携带对应查询参数的搜索页,渲染对应组件,控制台打印本次查询参数。

实际行为

触发搜索后控制台先正确打印新输入的查询参数,几乎立刻组件就会重渲染,且携带上一次的旧查询参数,导致同时加载新旧两次查询的结果展示给用户。

补充说明

该问题仅出现在当前已处于搜索页时发起新搜索的场景,刷新页面或从其他页面跳转至搜索页时表现正常。排查确认问题出在路由外层包裹的TransitionGroup和CSSTransition组件,移除这部分后表现符合预期。

问题原因

当处于搜索页发起新搜索时,路由参数更新生成新的location实例,TransitionGroup会同时保留旧location对应的CSSTransition节点(直到退场动画执行完毕)和新location对应的CSSTransition节点。两个节点内部的Switch都会匹配到/search路由,因此会同时渲染新旧两个Search组件实例:新实例拿到新查询参数,旧实例拿到旧查询参数,所以会先后打印新旧参数,看起来像是组件重复渲染携带旧参数。

解决方案

方案1(推荐,不影响转场效果)

给CSSTransition组件添加mountOnEnter和unmountOnExit属性,保证仅当前活跃的路由组件会被挂载,旧组件退场后立即卸载:

<CSSTransition 
  key={location.key} 
  classNames="slide" 
  timeout={2000}
  mountOnEnter // 仅进场动画开始时挂载组件
  unmountOnExit // 退场动画结束后立即卸载组件
>

方案2(适用于需要重叠转场效果的场景)

如果需要保留新旧页面同时存在的转场效果,在Search组件内部做参数校验,仅响应最新的查询参数:

const Search = ({ queries }) => {
  const latestLocation = useLocation();
  const latestQueries = getQueryObjects(latestLocation.search);

  useEffect(() => {
    // 仅当当前props的参数和最新路由参数一致时才执行搜索逻辑
    if (JSON.stringify(queries) === JSON.stringify(latestQueries)) {
      console.log("有效查询参数:", latestQueries);
      // 执行搜索请求、渲染逻辑
    }
  }, [queries, latestQueries])

  return <></>;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 22:27:03