已经有几个很好的答案,但我认为它们没有得到很好的解释,并且给出的一些方法包含一些可能会绊倒人们的陷阱。因此,我将介绍三种主要方式(外加一个题外话选项)并解释其优缺点。我之所以写这篇文章,主要是因为很多人推荐了选项 1,如果使用不当,该选项会出现很多潜在问题。
选项 1:在父级中进行条件渲染。
我不喜欢这种方法,除非您只打算渲染一次组件并将其留在那里。问题是每次切换可见性时都会导致从头开始创建组件的反应。
这是示例。 LogoutButton 或 LoginButton 在父 LoginControl 中有条件地呈现。如果你运行它,你会注意到构造函数在每次按钮点击时被调用。 https://codepen.io/Kelnor/pen/LzPdpN?editors=1111
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
现在 React 可以非常快速地从头开始创建组件。但是,它仍然必须在创建它时调用您的代码。因此,如果您的构造函数、componentDidMount、render 等代码很昂贵,那么它会显着减慢组件的显示速度。这也意味着您不能将它与您希望在隐藏时保留状态(并在显示时恢复)的有状态组件一起使用。一个优点是隐藏组件在被选中之前根本不会创建。因此隐藏组件不会延迟您的初始页面加载。在某些情况下,您可能希望在切换时重置有状态组件。在这种情况下,这是您的最佳选择。
选项 2:子项中的条件渲染
这将创建两个组件一次。如果组件被隐藏,则将其余的渲染代码短路。您还可以使用 visible 道具在其他方法中短路其他逻辑。注意 codepen 页面中的 console.log。 https://codepen.io/Kelnor/pen/YrKaWZ?editors=0011
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
<LoginButton isLoggedIn={isLoggedIn} onClick={this.handleLoginClick}/>
<LogoutButton isLoggedIn={isLoggedIn} onClick={this.handleLogoutClick}/>
</div>
);
}
}
class LogoutButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created logout button');
}
render(){
if(!this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Logout
</button>
);
}
}
class LoginButton extends React.Component{
constructor(props, context){
super(props, context)
console.log('created login button');
}
render(){
if(this.props.isLoggedIn){
return null;
}
return (
<button onClick={this.props.onClick}>
Login
</button>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
现在,如果初始化逻辑很快并且子节点是无状态的,那么您将不会看到性能或功能上的差异。但是,为什么要让 React 每次切换都创建一个全新的组件呢?但是,如果初始化成本很高,则每次切换组件时选项 1 都会运行它,这会在切换时减慢页面速度。选项 2 将在第一页加载时运行所有组件的初始化。减慢第一个负载。应再次注意。如果您只是根据条件显示组件一次而不是切换它,或者您希望它在切换时重置,那么选项 1 很好,可能是最好的选择。
但是,如果页面加载缓慢是一个问题,则意味着您在生命周期方法中拥有昂贵的代码,这通常不是一个好主意。您可以并且可能应该通过将昂贵的代码移出生命周期方法来解决页面加载缓慢的问题。将其移至由 ComponentDidMount 启动的异步函数,并让回调将其放入带有 setState() 的状态变量中。如果状态变量为 null 并且组件可见,则让渲染函数返回一个占位符。否则渲染数据。这样,页面将快速加载并在加载时填充选项卡。您还可以将逻辑移动到父级并将结果作为道具推送给子级。这样,您可以优先考虑首先加载哪些选项卡。或者缓存结果并仅在组件第一次显示时运行逻辑。
选项 3:类隐藏
类隐藏可能是最容易实现的。如前所述,您只需创建一个带有 display: none 的 CSS 类并根据 prop 分配该类。缺点是调用每个隐藏组件的整个代码,并且所有隐藏组件都附加到 DOM。 (选项 1 根本不创建隐藏的组件。选项 2 在隐藏组件时会缩短不必要的代码并从 DOM 中完全删除组件。)根据评论者所做的一些测试,这似乎在切换可见性方面更快其他答案,但我不能说。
选项 4:一个组件但更改 Props。或者可能根本没有组件并缓存 HTML。
这并不适用于每个应用程序,而且它不是主题,因为它不是关于隐藏组件,但对于某些用例来说,它可能是比隐藏更好的解决方案。假设您有标签。可以编写一个 React 组件,然后只使用道具来更改选项卡中显示的内容。您还可以将 JSX 保存到状态变量中,并使用 prop 来决定在渲染函数中返回哪个 JSX。如果必须生成 JSX,则执行此操作并将其缓存在父级中并将正确的一个作为道具发送。或者在child中生成并缓存在child的状态中,使用props来选择活跃的。