我们的 React 应用程序中有一个变量:
- 在 中定义为全局状态
App.js
,通过其 setter 方法全局传递给其他组件GlobalContext.Provider
,和 - 单独用作许多应用程序路由的路由参数。
下面是我们App.js
文件相关部分的简短代码片段:
import React, { useState, useEffect } from 'react';
import GlobalContext from './context/GlobalContext';
import OtherComponents...
function App() {
const [competition, setCompetition] = useState({ value: 15, label: 'Season 1' });
return (
<GlobalContext.Provider value={{ // Pass Global State Through To Entire App
competition, setCompetition
}}>
<Navbar />
<Switch>
<Route exact path='/' render={(props) => <HomePage {...props} />} />
<Route exact path='/stats' component={StatsPage} />
<Route exact path='/about' component={AboutUs} />
<Route exact path='/persons/:competitionId/ component={PersonsComponent} />
<Route exact path='/teams/:competitionId component={TeamsComponent} />
</Switch>
</GlobalContext.Provider>
);
}
export default App;
competition
全局状态有键value
和label
,然后competitionId
url 参数中的competition.value
值与值相同。
的全局状态值旨在使用小部件在组件competition
中进行更改。当这个小部件被切换时,全局状态被更新,并且钩子用于将应用推送到新路由,使用更新设置url 参数。<Navbar>
select
useHistory
competition.value
competitionId
competition
我们应用程序中的许多组件都需要for 的值,包括那些没有 url 参数的组件(例如在<HomePage>
组件中)。出于这个原因,我们觉得它需要作为一个全局变量,传递给所有其他组件。这对我们来说也很方便,因为该变量可以在任何地方通过useContext
钩子轻松访问。
但是,我们的 url 参数中似乎也需要该值。这些组件根据competitionId
传递的数据获取不同的数据,它们在 url 参数中是应用程序路由的很大一部分。
那么我们的问题是用户可以手动更改网站的 url,这可以更改 url 参数而无需更改变量的全局状态。通过手动更改 url,而不是使用select
小部件,全局状态和 url 参数然后不同步......
编辑:这是select
我们用来切换competition
值的组件(抱歉帖子越来越长)。这个选择在我们的导航栏中,并且在我们之外是全局可访问的<Switch>
:
function CompetitionSelect({ currentPath }) {
// Grab History In Order To Push To Selected Pages
let history = useHistory();
let { competition, setCompetition } = useContext(GlobalContext);
// Fetch Data on All Competitions (dropdown options)
const competitionInfosConfig = {};
const [competitionInfos, isLoading1, isError1] = useInternalApi('competitionInfo', [], competitionInfosConfig);
// Messy digging of competitionId out of current path.
let competitionIds = competitionInfos.map(row => row.competitionId);
let pathCompetitionId = null;
competitionIds.forEach(id => {
if (currentPath.includes(`/${id}/`)) {
pathCompetitionId = id;
}
});
// Messy Handling State/Params Out Of Sync
if (pathCompetitionId === null) {
console.log('Not a page where testing is needed');
}
else if (competition.value !== pathCompetitionId) {
console.log('WERE OUT OF SYNC...');
let correctGlobalState = { value: pathCompetitionId, label: 'Label Set' };
setCompetition(correctGlobalState);
} else {
console.log('IN SYNC: ', competition.value, ' == ', pathCompetitionId);
}
// Handle updating state + pushing to new route
const handleSelect = (event) => {
let oldPath = JSON.parse(JSON.stringify(history.location.pathname));
let newPath = '';
competitionIds.forEach(id => {
if (oldPath.includes(`/${id}/`)) {
newPath = oldPath.replace(`/${id}/`, `/${event.value}/`)
}
});
if (newPath !== '') {
setCompetition(event);
history.push(newPath);
}
};
// Create The Select
const competitionSelect =
(<Select
styles={appSelectStyles}
value={competition}
options={competitionInfos}
onChange={handleSelect}
placeholder={'Select Competition'}
/>);
return (
{competitionSelect}
);
}
export default CompetitionSelect;
这个组件在技术上确实解决了if, if else, else
子句中的不同步问题,但是无论何时setCompetition(correctGlobalState)
调用,React 都会抛出以下警告消息:
Warning: Cannot update a component (
应用) while rendering a different component (
比赛). To locate the bad setState() call inside
选择比赛选择, follow the stack trace as described...