我有一个App.jsx父组件和一个TopBar.js子组件。我想做的是从url中获取appId参数,然后把它传递给我的TopBar子组件。问题是我不知道如何使用match.params.appId,像CategoryPage (<CategoryPage categoryId = {match.params.categoryId} />
),因为我得到了一个错误信息:match is not defined,这似乎是正常的,因为我的子组件没有包含在一个 <Route>
组件。我看了一下文档,我只能找到经典的案例,有没有其他的方法来检索路由参数,比如说把它存储在一个状态中,以便重复使用呢?"先谢谢你的帮助或建议,我是这个项目的新手,我正在逐步整合代码的特殊性。
App.jsx
export default class App extends PureComponent {
static childContextTypes = {
apiKEY: PropTypes.string,
apiURL: PropTypes.string,
appName: PropTypes.string,
loginToken: PropTypes.string,
userId: PropTypes.string,
};
constructor(props) {
super(props);
const parsed = queryString.parse(window.location.search);
const state = {
apiKey: null,
appName: null,
fetchApiKeyError: null,
fetchApiKeyPending: false,
fetchApiKeyDone: false,
};
['auth_token', 'userId'].forEach((key) => {
state[key] = parsed[key] || localStorage.getItem(key);
if (parsed[key]) localStorage.setItem(key, parsed[key]);
});
this.state = state;
this.handleErrorAuth = this.handleErrorAuth.bind(this);
}
getChildContext() {
const {
auth_token: loginToken, userId, apiKey, appName,
} = this.state;
return {
apiURL: process.env.REACT_APP_API_URL,
loginToken,
userId,
apiKEY: apiKey,
appName,
};
}
renderRedirect = () => {
const isLogged = localStorage.auth_token && localStorage.userId;
// This is a private app, so we need to be logged all time
if (!isLogged) {
window.location = `${process.env.REACT_APP_AUTH_URL}?redirect_uri=${window.location}`;
return null;
}
return null;
}
fetchApiKey = (appId, authToken) => {
if (!authToken) return;
this.setState({ fetchApiKeyPending: true });
const storageKey = `apiKey_${appId}`;
const nameStorageKey = `name_${appId}`;
const apiKey = localStorage.getItem(storageKey);
const appName = localStorage.getItem(nameStorageKey);
// ApiKey and appName already in localStorage
if (apiKey && appName) {
this.setState({
fetchApiKeyPending: false,
fetchApiKeyDone: true,
apiKey,
appName,
});
return;
}
// flush all previous keys
Object.keys(localStorage)
.filter((val) => val.indexOf('apiKey_') + 1 || val.indexOf('name_') + 1)
.forEach((val) => localStorage.removeItem(val));
// get ApiKey
fetch(`${process.env.REACT_APP_API_URL}/apps/${appId}/infos`, {
headers: {
Authorization: `Bearer ${authToken}`,
},
}).then((data) => {
if (!data.ok) throw new Error(data.status);
return data;
})
.then((data) => data.json())
.then(({ key, name }) => {
localStorage.setItem(storageKey, key);
localStorage.setItem(nameStorageKey, name);
this.setState({
fetchApiKeyPending: false,
fetchApiKeyDone: true,
fetchApiKeyError: null,
apiKey: key,
appName: name,
});
})
.catch((e) => this.setState({
fetchApiKeyPending: false,
fetchApiKeyDone: true,
fetchApiKeyError: e,
apiKey: null,
appName: null,
}));
};
getLastAppId = () => {
const storageKey = Object.keys(localStorage).filter(
(val) => val.indexOf('apiKey_') + 1,
)[0];
return storageKey ? storageKey.split('apiKey_')[1] : null;
};
switch = (location) => {
const {
fetchApiKeyPending,
fetchApiKeyDone,
apiKey,
auth_token: loginToken,
fetchApiKeyError,
} = this.state;
return (
<Switch location={location}>
{apiKey && [
<Route
key="1"
path="/:appId/categories/:categoryId/new_article"
render={({ match }) => (
<DefaultLayout>
<NewArticlePage categoryId={match.params.categoryId} />
</DefaultLayout>
)}
/>,
<Route
key="2"
path="/:appId/articles/:articleId"
render={({ match }) => (
<DefaultLayout>
<ModifyArticlePage articleId={match.params.articleId} />
</DefaultLayout>
)}
/>,
<Route
key="3"
path="/:appId/createCategory"
render={({ match }) => (
<CenteredLayout
backButtonProps={{
to: `/${match.params.appId}/categories`,
}}
>
<NewCategoryPage />
</CenteredLayout>
)}
apikey
/>,
<Route
key="4"
path="/:appId/categories/:categoryId/modify"
render={({ match }) => (
<CenteredLayout>
<NewCategoryPage categoryId={match.params.categoryId} />
</CenteredLayout>
)}
/>,
<Route
key="5"
path="/:appId/categories/:categoryId"
render={({ match }) => (
<CenteredLayout
backButtonProps={{
to: `/${match.params.appId}/categories`,
}}
>
<CategoryPage categoryId={match.params.categoryId} />
</CenteredLayout>
)}
/>,
<Route key="6" path="/:appId/categories" component={CategoriesPage} />,
]}
<Route path="/welcome" component={WelcomePage} />
<Route path="/app_not_found" render={() => <AppNotFoundPage titleKey="press" />} />
<Route
path="/:appId/"
exact={false}
render={({ match }) => {
if (fetchApiKeyError) {
return <Redirect to="/app_not_found" />;
}
if (!fetchApiKeyDone || fetchApiKeyPending) {
return (
[
<OnMountExecuter
key="1"
execute={this.fetchApiKey}
params={[match.params.appId, loginToken]}
/>,
<Loading key="2" style={{ fontSize: 36 }} />,
]
);
}
return <Redirect to={`/${match.params.appId}/categories`} />;
}}
/>
<Route
path="/"
render={() => {
if (localStorage.auth_token && localStorage.userId) {
const appId = this.getLastAppId();
if (appId) {
return <Redirect to={`/${appId}/categories`} />;
}
}
return <Redirect to="/welcome" />;
}}
/>
</Switch>
);
};
handleErrorAuth() {
this.setState({
auth_token: null,
userId: null,
});
localStorage.clear();
}
renderContent(location) {
return (
<HttpsRedirect>
<IntlProvider locale={language} messages={messages[language]}>
<div id="container">
<div id="content-container">
<AuthorizeChecker onError={this.handleErrorAuth} />
<UserContextProvider>
<UserContext.Consumer>
{(user) => <TopBar user={user} />}
</UserContext.Consumer>
{this.switch(location)}
</UserContextProvider>
</div>
</div>
</IntlProvider>
</HttpsRedirect>
);
}
render() {
return (
<BrowserRouter>
<Route
render={({ location }) => this.renderRedirect(location) || this.renderContent(location)}
/>
</BrowserRouter>
);
}
}
你可以使用react router创建的上下文从Router组件的任何一个大子组件中访问match对象,它可以在下面找到,context =>router=>match。从那里你可以对它做任何你想做的事情。
你可以在 this.context
在任何基于类的组件中,在一个钩子组件中,你必须使用钩子。useContext
另外,请注意,新版本的react router有一些钩子,可能会对你有所帮助,如useRouteMatch
和 useParams
.