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
相关产品推荐
相关产品推荐

