React跨模块路由导航:从medialists跳转至search模块的实现
React 模块跳转实现方案咨询
我在React应用中新增了medialists模块,应用主路径为localhost/web;现有search组件,搜索条目后路径为localhost/web/search#/itemname。现在需要从medialists模块(路径localhost/web/medialists)的条目跳转到search模块并传入itemname,纠结该用history还是useNavigate,附上组件代码,求可行实现方案。
组件代码
return ( <ClayList.Item flex key={index}> <BrowserRouter> <Link aria-label={t('item.lnkRecord.label')} className="col-md-10 my-lists-card-details-link" title={t('item.lnkRecord.title')} to={searchResultPage} > <ClayList.ItemField expand> <div className="card-row"> {entity.almaId && entity.contextId && <div className="arena-search-result-card autofit-col"> {formatImage(image, entity.name, classNames( 'arena-item-image', 'card-item-first', ))} </div> } <div className="autofit-col autofit-col-expand autofit-col-gutters"> <section className="autofit-section"> <h3 className="card-title text-truncate">{entity.name}</h3> {entity.contributor && ( <ClayCard.Row className="arena-field-contributor"> <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {MiscUtil.recordValues(entity.contributor, 'author')} </ClayCard.Description> </ClayCard.Row> </ClayCard.Row> )} {entity.id && ( <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {entity.id} </ClayCard.Description> </ClayCard.Row> )} </section> </div> </div> </ClayList.ItemField> </Link> </BrowserRouter> <ClayList.ItemField className="col-md-2"> <ClayButton displayType="unstyled" onClick={() => onClickDeleteItem(sourceId, entity.id)}> {t('item.delete.button.title')} </ClayButton> </ClayList.ItemField> </ClayList.Item> );
解决方案
1. 先修正路由上下文问题
你当前代码将<BrowserRouter>嵌套在列表项内部是错误的——整个React应用只需要一个根级的<BrowserRouter>,应将其移至应用最外层(比如index.js或根组件中),否则路由相关的组件/钩子无法获取正确的路由上下文。
2. 跳转方式选择
基于React Router版本的选型
- React Router v5:使用
useHistory钩子获取history对象,或用withRouter高阶组件; - React Router v6:官方推荐使用
useNavigate钩子,useHistory已被废弃。
方案一:用Link组件(静态跳转首选)
直接构造包含itemname哈希值的目标路径,通过Link组件实现跳转:
// 构造目标路径,entity.name即为要传入的itemname,编码避免特殊字符问题 const searchResultPage = `/search#/${encodeURIComponent(entity.name)}`; // 移除内部的<BrowserRouter>,保留Link组件 return ( <ClayList.Item flex key={index}> <Link aria-label={t('item.lnkRecord.label')} className="col-md-10 my-lists-card-details-link" title={t('item.lnkRecord.title')} to={searchResultPage} > {/* 原列表内容保持不变 */} <ClayList.ItemField expand> <div className="card-row"> {entity.almaId && entity.contextId && <div className="arena-search-result-card autofit-col"> {formatImage(image, entity.name, classNames( 'arena-item-image', 'card-item-first', ))} </div> } <div className="autofit-col autofit-col-expand autofit-col-gutters"> <section className="autofit-section"> <h3 className="card-title text-truncate">{entity.name}</h3> {entity.contributor && ( <ClayCard.Row className="arena-field-contributor"> <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {MiscUtil.recordValues(entity.contributor, 'author')} </ClayCard.Description> </ClayCard.Row> </ClayCard.Row> )} {entity.id && ( <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {entity.id} </ClayCard.Description> </ClayCard.Row> )} </section> </div> </div> </ClayList.ItemField> </Link> <ClayList.ItemField className="col-md-2"> <ClayButton displayType="unstyled" onClick={() => onClickDeleteItem(sourceId, entity.id)}> {t('item.delete.button.title')} </ClayButton> </ClayList.ItemField> </ClayList.Item> );
方案二:用useNavigate(动态跳转/需逻辑处理场景)
如果跳转前需要处理itemname(比如编码、校验),或需要通过按钮点击触发跳转,使用useNavigate钩子:
import { useNavigate } from 'react-router-dom'; // 在组件内部调用钩子 const navigate = useNavigate(); // 定义跳转处理函数 const handleGoToSearch = () => { const encodedItemName = encodeURIComponent(entity.name); navigate(`/search#/${encodedItemName}`); }; return ( <ClayList.Item flex key={index}> {/* 将Link替换为可点击容器,触发跳转函数 */} <div onClick={handleGoToSearch} aria-label={t('item.lnkRecord.label')} className="col-md-10 my-lists-card-details-link" title={t('item.lnkRecord.title')} style={{ cursor: 'pointer' }} > {/* 原列表内容保持不变 */} <ClayList.ItemField expand> <div className="card-row"> {entity.almaId && entity.contextId && <div className="arena-search-result-card autofit-col"> {formatImage(image, entity.name, classNames( 'arena-item-image', 'card-item-first', ))} </div> } <div className="autofit-col autofit-col-expand autofit-col-gutters"> <section className="autofit-section"> <h3 className="card-title text-truncate">{entity.name}</h3> {entity.contributor && ( <ClayCard.Row className="arena-field-contributor"> <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {MiscUtil.recordValues(entity.contributor, 'author')} </ClayCard.Description> </ClayCard.Row> </ClayCard.Row> )} {entity.id && ( <ClayCard.Row className="arena-field-published-edition"> <ClayCard.Description displayType="title"> {entity.id} </ClayCard.Description> </ClayCard.Row> )} </section> </div> </div> </ClayList.ItemField> </div> <ClayList.ItemField className="col-md-2"> <ClayButton displayType="unstyled" onClick={() => onClickDeleteItem(sourceId, entity.id)}> {t('item.delete.button.title')} </ClayButton> </ClayList.ItemField> </ClayList.Item> );
3. 关于history vs useNavigate的总结
- 若使用React Router v6,直接用
useNavigate即可,history相关API已被移除; - 若仍在使用v5,可通过
useHistory获取history对象,调用history.push(/search#/${encodedItemName})实现跳转,但建议逐步升级到v6,适配官方新API。
内容的提问来源于stack exchange,提问作者Hani
相关产品推荐
相关产品推荐

