基于 url 的页面更新的 React useLocation 钩子的替代方案?
Alternative to React useLocation hook for page update based on url?
这是一个棘手的问题。
一些信息:
我可以有多个 collection 多张图片和每个 header 的特定徽标 collection。
我的collectionurl看起来像这样/collection/{collectionId}/item/{itemId}
const MyApp = () => {
// unrelated code
return(
<BrowserRouter>
<SiteLayout>
</BrowserRouter>
)
}
和
const SiteLayout = () => {
location = useLocation()
collectionId = location.pathname.split('/')[2] //gives me the collection Alias
const [collData, setCollData] = useState()
useEffect(() => {
const getCollectionData = async() => {
//fetch collection data
//setCollData(fetched data)
}
getCollectionData()
}, [collectionId])
return(
<CollectionContext.Provider value={collData}>
<div className='mainContainer'>
<div className='headerContainer'>
<Header/>
</div>
<div className='mainContent'>
<Route exact path='/collection/:collectionId' component={CollectionViewPage}>
<Route exact path='/collection/:collectionId/item/:itemId' component={ItemViewPage}
</div>
<div className='footerContainer'>
<Footer/>
</div>
</div>
</CollectionContext.Provider>
)
}
export default SiteLayout
我的想法:
- 我是这样设置的,因为我不能在
<BrowserRouter>
之外使用 useLocation()
并且我想更新我的 collectionContext(其中包含有关 collection 我在,包括 collection 特定的 header 徽标)当 collectionId
更改时,它是 location
的一部分
我的问题:
当我从一个项目移动到另一个项目时,在一个collection、header和其他所有东西仍然re-renders,例如
来自 -- /collection/
collection1/item
/1
至 -- /collection/
collection1/item
/2
我的想法: 这个 header 不应该更新,因为 collectionId
从来没有改变过。 但是,当我在 React 开发工具中查看 Profiler 时,它说 Header 发生了变化,因为 parent 发生了变化,一直到 <Route>
它改变了,因为它的状态 (location
) 改变了。
我在寻找什么:如何重构它以在 collectionId
更改时更新,而不是每次 location
更改时更新?我需要这个是因为(很多事情,但为了简单起见)我希望 header 在我更改 collections 时更新,以便它可以使用正确的徽标,但我想阻止 <Header>
来自 re-rendering 如果我在 内导航 collection.
我试过的其他方法:
我试过删除 useLocation
并使用 window.location.pathname
但没有任何更新.
已解决
使用@HMR 的建议来回答原始问题:
If Header and CollectionViewPage are functional components maybe you can wrap [them] in React.memo If they still re render [...] look at what you need and maybe create a container that only picks what you actually need...
实施:
通过添加一个中间层,我可以拆分 location
,然后根据我想要的任何部分记忆 SiteLayout
。
const MyApp = () => {
// unrelated code
return(
<BrowserRouter>
<LocationPartition>
</BrowserRouter>
)
}
export default MyApp
在 LocationPartition
中的位置(可能使用更好的名称):
const LocationPartition = () => {
const location = useLocation()
const collectionId = location.pathname.split('/')[2]
return(
<SiteLayout collection={collectionId}/>
)
}
export default LocationPartition
所以在 SiteLayout
中,我现在可以根据从 LocationPartition
传递的 collection
道具进行记忆
const SiteLayout = memo(({collection})) => {
const [collData, setCollData] = useState()
useEffect(() => {
const getCollectionData = async() => {
//fetch collection data
//setCollData(fetched data)
}
getCollectionData()
}, [collection])
return(
<CollectionContext.Provider value={collData}>
<div className='mainContainer'>
<div className='headerContainer'>
<Header/>
</div>
<div className='mainContent'>
<Route exact path='/collection/:collectionId' component={CollectionViewPage}>
<Route exact path='/collection/:collectionId/item/:itemId' component={ItemViewPage}
</div>
<div className='footerContainer'>
<Footer/>
</div>
</div>
</CollectionContext.Provider>
)
}
export default SiteLayout
瞧瞧 ... React Profiler 现在只在我想要的地方显示重新渲染!
这是一个棘手的问题。
一些信息:
我可以有多个 collection 多张图片和每个 header 的特定徽标 collection。
我的collectionurl看起来像这样/collection/{collectionId}/item/{itemId}
const MyApp = () => {
// unrelated code
return(
<BrowserRouter>
<SiteLayout>
</BrowserRouter>
)
}
和
const SiteLayout = () => {
location = useLocation()
collectionId = location.pathname.split('/')[2] //gives me the collection Alias
const [collData, setCollData] = useState()
useEffect(() => {
const getCollectionData = async() => {
//fetch collection data
//setCollData(fetched data)
}
getCollectionData()
}, [collectionId])
return(
<CollectionContext.Provider value={collData}>
<div className='mainContainer'>
<div className='headerContainer'>
<Header/>
</div>
<div className='mainContent'>
<Route exact path='/collection/:collectionId' component={CollectionViewPage}>
<Route exact path='/collection/:collectionId/item/:itemId' component={ItemViewPage}
</div>
<div className='footerContainer'>
<Footer/>
</div>
</div>
</CollectionContext.Provider>
)
}
export default SiteLayout
我的想法:
- 我是这样设置的,因为我不能在
<BrowserRouter>
之外使用useLocation()
并且我想更新我的 collectionContext(其中包含有关 collection 我在,包括 collection 特定的 header 徽标)当collectionId
更改时,它是location
的一部分
我的问题:
当我从一个项目移动到另一个项目时,在一个collection、header和其他所有东西仍然re-renders,例如
来自 --
/collection/
collection1/item
/1
至 --/collection/
collection1/item
/2我的想法: 这个 header 不应该更新,因为
collectionId
从来没有改变过。 但是,当我在 React 开发工具中查看 Profiler 时,它说 Header 发生了变化,因为 parent 发生了变化,一直到<Route>
它改变了,因为它的状态 (location
) 改变了。
我在寻找什么:如何重构它以在 collectionId
更改时更新,而不是每次 location
更改时更新?我需要这个是因为(很多事情,但为了简单起见)我希望 header 在我更改 collections 时更新,以便它可以使用正确的徽标,但我想阻止 <Header>
来自 re-rendering 如果我在 内导航 collection.
我试过的其他方法:
我试过删除 useLocation
并使用 window.location.pathname
但没有任何更新.
已解决
使用@HMR 的建议来回答原始问题:
If Header and CollectionViewPage are functional components maybe you can wrap [them] in React.memo If they still re render [...] look at what you need and maybe create a container that only picks what you actually need...
实施:
通过添加一个中间层,我可以拆分 location
,然后根据我想要的任何部分记忆 SiteLayout
。
const MyApp = () => {
// unrelated code
return(
<BrowserRouter>
<LocationPartition>
</BrowserRouter>
)
}
export default MyApp
在 LocationPartition
中的位置(可能使用更好的名称):
const LocationPartition = () => {
const location = useLocation()
const collectionId = location.pathname.split('/')[2]
return(
<SiteLayout collection={collectionId}/>
)
}
export default LocationPartition
所以在 SiteLayout
中,我现在可以根据从 LocationPartition
collection
道具进行记忆
const SiteLayout = memo(({collection})) => {
const [collData, setCollData] = useState()
useEffect(() => {
const getCollectionData = async() => {
//fetch collection data
//setCollData(fetched data)
}
getCollectionData()
}, [collection])
return(
<CollectionContext.Provider value={collData}>
<div className='mainContainer'>
<div className='headerContainer'>
<Header/>
</div>
<div className='mainContent'>
<Route exact path='/collection/:collectionId' component={CollectionViewPage}>
<Route exact path='/collection/:collectionId/item/:itemId' component={ItemViewPage}
</div>
<div className='footerContainer'>
<Footer/>
</div>
</div>
</CollectionContext.Provider>
)
}
export default SiteLayout
瞧瞧 ... React Profiler 现在只在我想要的地方显示重新渲染!