防止组件在布线时取消安装

Prevent Component from Unmouting when Routing

我有一个子组件,当路由远离父组件时我需要保留它。 在网络上搜索,大多数答案都是关于 "react-router",但我使用的是 "react-router-dom"(又名 React Router V4)。 我对反应还很陌生,我认为这应该很简单,比如将组件标记为不被卸载或类似的东西。

import Home from "./home/Home";
import Top from "./top/Top";

export default class Layout extends React.Component{
    render(){
        return(
            <Router>
                <div class="wrp">
                    <Menu/>
                    <section class="left">
                        <Header/>
                        <Switch>
                            <Route exact path="/" component={Home} />
                            <Route exact path="/top" component={Top} />
                        </Switch>
                        <Footer/>
                    </section>
                </div>
            </Router>
        );
    }
}

在 "Home" 组件中有一个子组件需要保留在 DOM 中。

根据要求 Home 和 child 组件代码:

import React from "react";

import Sponsor from "./Sponsor";
import Feature from "./Feature";
import Top from "./Top";
import Discord from "./Discord";
import Middle from "./Middle";

export default class Home extends React.Component{
    render(){
        return(
            <section class="main_wrp">
                <Sponsor/>
                <div class="bulk">
                    <Feature/>
                    <div class="co6">
                        <Top/>
                        <Discord/>
                    </div>
                    <Middle/>
                </div>
            </section>
        );
    }
}

子组件(Discord):

import React from "react";

export default class Discord extends React.Component{

    render(){
        return(

            <div class="DiscordViewer">
                <embed height='500px'src='https://somelink.com' />
            </div>
        );
    }
}

一种可行的方法是将 <Discord\> 组件移出主页。 因此在布局中,您创建 <Discord\> 组件并将其作为子组件传递给所有其他组件。

布局

export default class Layout extends React.Component{
    render(){
        let discord = <Discord/>;
        return(
            <Router>
                <div class="wrp">
                    <Menu/>
                    <section class="left">
                        <Header/>
                        <Switch>
                            <Route exact path="/" render={()=><Home>{discord}</Home>} />
                            <Route exact path="/top" render={()=><Top>{discord}</Top>} />
                        </Switch>
                        <Footer/>
                    </section>
                </div>
            </Router>
        );
    }
}

你的 Home 组件会变成

export default class Home extends React.Component{
    render(){
        return(
            <section class="main_wrp">
                <Sponsor/>
                <div class="bulk">
                    <Feature/>
                    <div class="co6">
                        <Top/>
                        {this.props.children}
                    </div>
                    <Middle/>
                </div>
            </section>
        );
    }
}

您还可以将 <Discord/> 组件作为命名道具传递给所有需要显示它的组件,而不是将其作为子组件传递。

在不修改组件树的情况下,在离开其父节点导航后保留叶节点组件(本例中为 Discord)将违反树的定义。您不能简单地使用 CSS 隐藏和重新定位组件,您必须对从最近的父节点到该叶节点的树的整个分支执行此操作。这可能会导致不必要的复杂性和性能不佳,尽管它可以像这样执行:

const HomeWrapper = withRouter(props => (
  <div style={props.location.pathname !== '/' ? {display: 'none'} : {}}>
    <Home {...props} />
  <div>
))

<HomeWrapper />
<Switch>
  <Route exact path="/top" component={Top} />
  <Route exact path="/other" component={OtherComponent} />
</Switch>

如果您的组件结构灵活,您可以将 Discord 组件从主路由移到隐藏的 div。然后,您将使用非反应 DOM 操作在外部隐藏 div 和主页中显示的 div 之间移动不和谐。在您的 Home 组件中,您可以呈现如下所示的 DiscordWrapper 组件。您需要从 shouldComponentUpdate return false 来阻止更新。 :

<div id='discord-wrapper-external' style={{display: 'none'}}>
  <Discord id='discord-dom-node'/>
</div>

class DiscordWrapper extends React.Component {
  componentDidMount() {
    this.el = document.getElementById('discord-dom-node')
    document.getElementById('discord-wrapper-internal').appendChild(this.el)
  }
  shouldComponentUpdate() {
    return false
  }
  componentWillUnmount() {
    document.getElementById('discord-wrapper-external').appendChild(this.el)
  }
  render() {
    return <div id='discord-wrapper-internal' />
  }