我正在尝试使用React构建一个单页应用程序。当前,我有一个名为App
的组件,它由ReactDOM
渲染,它包含导航,然后是要在导航组件之后渲染的组件(或页面)。
import React from 'react';
import Navigation from './Navigation';
import NavigationLink from './NavigationLink';
import Home from './Home';
import About from './About';
const App = () => (
<div>
<Navigation>
<NavigationLink onClick={ ... }>Home</NavigationLink>
<NavigationLink onClick={ ... }>About</NavigationLink>
</Navigation>
<Home />
</div>
);
export default App;
我希望能够选择“关于”链接,并将Home
组件更新为About
。我正在寻找解决此问题的解决方案,以解决两个以上的要素(或页面)的问题。
这是我当前的有效代码,尽管解决方案很差。
import React, { Component } from 'react';
import {
NavItem,
NavLink,
UncontrolledDropdown,
DropdownToggle,
DropdownMenu,
DropdownItem,
} from 'reactstrap';
import firebase from '../../utils/firebase';
import Navigation from '../../components/Navigation/Navigation';
import Login from '../Login/Login';
import Profile from '../Profile/Profile';
import Home from '../Home/Home';
class App extends Component {
state = {
currentShowingPage: 0,
currentlyLoggedInUser: null,
isLoginModalOpen: false,
};
componentDidMount = () => {
firebase.auth().onAuthStateChanged((currentUser) => {
if (currentUser) {
this.setState({
currentlyLoggedInUser: currentUser,
isLoginModalOpen: false,
});
} else {
this.setState({
currentlyLoggedInUser: null,
});
}
});
};
handleLoginModalToggle = () => {
this.setState((previousState) => ({
isLoginModalOpen: !previousState.isLoginModalOpen,
}));
};
render = () => {
let currentShowingPageComponent;
const {
isLoginModalOpen,
currentlyLoggedInUser,
currentShowingPage,
} = this.state;
if (currentShowingPage === 0) {
currentShowingPageComponent = <Home />;
} else if (currentShowingPage === 1) {
currentShowingPageComponent = <Profile />;
}
return (
<div>
<Login
isModalOpen={isLoginModalOpen}
modalToggler={this.handleLoginModalToggle}
/>
<Navigation>
{currentlyLoggedInUser ? (
<UncontrolledDropdown nav>
<DropdownToggle nav caret>
{currentlyLoggedInUser.email}
</DropdownToggle>
<DropdownMenu right>
<DropdownItem
onClick={() =>
this.setState({
currentShowingPage: 1,
})
}
>
Profile
</DropdownItem>
<DropdownItem disabled>Expeditions</DropdownItem>
<DropdownItem divider />
<DropdownItem onClick={() => firebase.auth().signOut()}>
Sign Out
</DropdownItem>
</DropdownMenu>
</UncontrolledDropdown>
) : (
<NavItem>
<NavLink onClick={this.handleLoginModalToggle}>Login</NavLink>
</NavItem>
)}
</Navigation>
{currentShowingPageComponent}
</div>
);
};
}
export default App;
答案 0 :(得分:3)
这只是切换组件的一个非常简单的例子,有很多方法可以实现,我相信您会获得更好的实践答案,但希望可以给您一些想法。 (以防不明显,您可以通过调用setState({currentComponent:'compX'})来使用它)
getComponent(){
let component;
switch (this.state.currentComponent){
case 'compA' :
component = <CompA/>;
break;
case 'compB' :
component = <CompB/>;
break;
case 'compC' :
component = <CompC/>;
break;
case 'compD' :
component = <CompD/>;
break;
}
return component;
}
render(){
return(
<div>
{this.getComponent()}
</div>
);
}
答案 1 :(得分:2)
TLDR:在构建应用程序主体的render()方法内部,动态插入与您要向用户显示的当前组件/模块/页面一致的React组件。 / strong>
您绝对可以使用React Router。它是公认的并且被广泛使用。但是,如果您愿意的话,完全可以在没有React Router的情况下完成此操作。我也在构建一个单页应用程序,并且也在交换您所描述的组件。这是完成此任务的两个主要文件:
template.default.js:
INSERT INTO X (output) VALUES ('awesomeness');
and navigation.left.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);
答案 2 :(得分:2)
(即将提供无耻的插头)
我写了一篇关于动态加载React组件的博客文章。
https://www.slightedgecoder.com/2017/12/03/loading-react-components-dynamically-demand/#case1
请参阅第一种情况,即使用动态import()动态加载组件。
要点是,您创建一个component
作为状态,并根据传递给addComponent
的类型(我博客中模块的名称)对其进行更新。
这种方法的好处是,浏览器不会下载不需要的组件。
对于您而言,如果没有人点击About
页,则该组件将永远不会下载。
addComponent = async type => {
console.log(`Loading ${type} component...`);
import(`./components/${type}.js`)
.then(component =>
this.setState({
components: this.state.components.concat(component.default)
})
)
.catch(error => {
console.error(`"${type}" not yet supported`);
});
};