TLDR:在构建应用主体的 render() 方法中,动态插入与您尝试向用户显示的当前组件/模块/页面一致的 React 组件。
你绝对可以使用 React Router。它已经成熟并被广泛使用。但是如果你愿意,你完全可以在没有 React Router 的情况下做到这一点。我还在构建一个单页应用程序,并且我还按照您的描述替换了组件。以下是完成此任务的两个主要文件:
template.default.js:
// lots o' imports up here...
// styles/themes that are needed to support the drawer template
class DefaultTemplate extends React.Component {
constructor(props) {
super(props);
this.state = {
mainHeight : 200,
mobileOpen : false,
toolbarSpacerHeight : 100,
};
session[the.session.key.for.DefaultTemplate] = this;
session.browser = detect();
}
getModule() {
// here I'm switching on a session variable (which is available throughout the entire app to determine which module ID the user has currently chosen
// notice that the return values are the dynamic React components that coincide with the currently-chosen module ID
switch (session.DisplayLayer.state.moduleId) {
case the.module.id.for.home:
return <HomeModule/>;
case the.module.id.for.lists:
return <ListsModule/>;
case the.module.id.for.login:
return <LogInModule/>;
case the.module.id.for.logout:
return <LogOutModule/>;
case the.module.id.for.register:
return <RegisterModule/>;
case the.module.id.for.roles:
return <RolesModule/>;
case the.module.id.for.teams:
return <TeamsModule/>;
case the.module.id.for.users:
return <UsersModule/>;
default:
return null;
}
}
handleDrawerToggle = () => {
this.setState({mobileOpen : !this.state.mobileOpen});
};
render() {
// the module is dynamically generated every time a render() is invoked on this template module
const module = this.getModule();
return (
<div className={classes.root} style={{height : the.style.of.percent.hundred}}>
<AppBar className={classes.appBar} style={{backgroundColor : the.color.for.appBar}}>
<Toolbar>
<IconButton
aria-label={the.ariaLabel.openDrawer}
className={classes.navIconHide}
color={the.style.of.inherit}
onClick={this.handleDrawerToggle}
>
<MenuIcon/>
</IconButton>
<FontAwesome name={the.icon.for.palette} style={{marginRight : '10px', fontSize : the.style.of.onePointFiveEms}}/>
<Typography variant={the.variant.of.title} color={the.style.of.inherit} noWrap>
<TranslatedText english={'Groupware'}/>.<TranslatedText english={'Studio'}/>
</Typography>
<LanguageMenu
containerStyle={{marginLeft : the.style.of.margin.auto}}
onClose={event => {this.updateLanguage(event)}}
selectedLanguageId={db.getItem(the.db.item.for.languageId)}
/>
</Toolbar>
</AppBar>
<Hidden mdUp>
<Drawer
anchor={theme.direction === the.direction.of.rightToLeft ? the.direction.of.right : the.direction.of.left}
classes={{paper : classes.drawerPaper}}
ModalProps={{keepMounted : true}}
onClose={this.handleDrawerToggle}
open={this.state.mobileOpen}
variant={the.variant.of.temporary}
>
{drawer}
</Drawer>
</Hidden>
<Hidden smDown implementation={the.implementation.of.css}>
<Drawer
classes={{paper : classes.drawerPaper}}
open
variant={the.variant.of.permanent}
>
{drawer}
</Drawer>
</Hidden>
<main
className={classes.content}
ref={main => this.main = main}
style={{backgroundColor : the.color.for.module.background}}
>
<div
className={classes.toolbar}
ref={toolbarSpacer => this.toolbarSpacer = toolbarSpacer}
/>
{/*
here is where that dynamically-generated module is rendered inside the template
*/}
{module}
</main>
</div>
);
}
}
export default withStyles(styles, {withTheme : true})(DefaultTemplate);
还有navigation.left.js:
// lots o' imports up here
class LeftNavigation extends React.Component {
listButtons = [];
// this object controls the configuration of the nav links that show on the left side of the template
navigation = {
isLoggedIn : [
{
icon : the.icon.for.home,
isFollowedByDivider : false,
label : the.label.for.home,
moduleId : the.module.id.for.home,
},
{
icon : the.icon.for.powerOff,
isFollowedByDivider : true,
label : the.label.for.logOut,
moduleId : the.module.id.for.logout,
},
{
icon : the.icon.for.orderedList,
isFollowedByDivider : false,
label : the.label.for.lists,
moduleId : the.module.id.for.lists,
},
{
icon : the.icon.for.roles,
isFollowedByDivider : false,
label : the.label.for.roles,
moduleId : the.module.id.for.roles,
},
{
icon : the.icon.for.teams,
isFollowedByDivider : false,
label : the.label.for.teams,
moduleId : the.module.id.for.teams,
},
{
icon : the.icon.for.users,
isFollowedByDivider : false,
label : the.label.for.users,
moduleId : the.module.id.for.users,
},
],
isLoggedOut : [
{
icon : the.icon.for.home,
isFollowedByDivider : false,
label : the.label.for.home,
moduleId : the.module.id.for.home,
},
{
icon : the.icon.for.powerOff,
isFollowedByDivider : false,
label : the.label.for.logIn,
moduleId : the.module.id.for.login,
},
{
icon : the.icon.for.registered,
isFollowedByDivider : false,
label : the.label.for.register,
moduleId : the.module.id.for.register,
},
],
};
populateListButtons() {
// here we are generating an array of ListButtons that will comprise the left-hand navigation
this.listButtons = [];
let buttonConfigs = [];
switch (db.getItem(the.db.item.for.isLoggedIn)) {
case true:
buttonConfigs = this.navigation.isLoggedIn;
break;
case false:
buttonConfigs = this.navigation.isLoggedOut;
break;
default:
return;
}
buttonConfigs.forEach(buttonConfig => {
let buttonIsEnabled = true;
let fontAwesomeStyle = {fontSize : the.style.of.onePointFiveEms};
let listItemStyle = {};
let textStyle = {};
switch (buttonConfig.label) {
case the.label.for.logIn:
fontAwesomeStyle[the.style.property.name.of.color] = the.color.for.success;
break;
case the.label.for.logOut:
fontAwesomeStyle[the.style.property.name.of.color] = the.color.for.error;
break;
default:
if (session.DisplayLayer.state.moduleId === buttonConfig.moduleId) {
fontAwesomeStyle[the.style.property.name.of.color] = the.color.for.white.text;
} else {
fontAwesomeStyle[the.style.property.name.of.color] = the.color.for.headerBar;
}
break;
}
if (session.DisplayLayer.state.moduleId === buttonConfig.moduleId) {
buttonIsEnabled = false;
listItemStyle[the.style.property.name.of.backgroundColor] = the.color.for.selectedLeftNavButtonOrange;
textStyle[the.style.property.name.of.color] = the.color.for.white.text;
}
this.listButtons.push(
<ListItem
button={buttonIsEnabled}
key={`${buttonConfig.label}-listItem`}
// notice that when one of the left nav links is clicked, we are updating the moduleId value in session,
// which dynamically determines which module shows up in the center panel
onClick={() => session.DisplayLayer.updateModuleId(buttonConfig.moduleId)}
style={listItemStyle}
>
<ListItemIcon>
<FontAwesome name={buttonConfig.icon} style={fontAwesomeStyle}/>
</ListItemIcon>
<TranslatedText english={buttonConfig.label} style={textStyle}/>
</ListItem>,
);
if (buttonConfig.isFollowedByDivider) {
this.listButtons.push(<Divider key={`${buttonConfig.label}-divider`}/>);
}
});
}
render() {
// dynamically generate the array of left nav buttons before rendering the links
this.populateListButtons();
return <List style={{paddingTop : the.style.of.pixels.zero}}>{this.listButtons}</List>;
}
}
export default LeftNavigation;