为什么React Router导致我的组件重新挂载,而不是只重新渲染?

5
我正在使用React Router创建一个应用程序,其中包含多个路由,包括一个名为Uploader的组件,用于上传视频,以及一个名为Videos的组件,用于查看视频。 视频页面将某些评论存储为状态,并希望在整个应用程序打开期间保留在该页面上。但是,React Router似乎导致每个组件重新挂载而不是重新渲染,这会导致每次重新路由回Video组件时将我的状态重置为初始空值。我在我的组件中使用render方法而不是component方法,因此我不理解为什么会发生这种情况。有人知道产生这种情况的原因吗?
以下是进行路由的主要应用程序:
class App extends React.Component{

    constructor(props){
        super(props)
        var fileNames
        var files
        var fileSelected
        this.state={fileSelected:null}
     }

    getFileFromChild= (uploadedFiles)=> {
         this.files = uploadedFiles

    }

    fileButtonClicked= (index)=> {
        //extract file chosen by user based on button click
        this.setState({fileSelected: this.files[0][index]})

    }

    render(){
        //destructuring props in class component
        const {user} = this.props;
    return(

        <Router>
            <div className = "nav-bar">
                <Nav/>
                <Switch>
                    <Route path='/' exact render={()=> <HomePage />
                    }/> 
                    <Route path='/videos' render={()=> <Videos files= {this.files} fileSelected={this.state.fileSelected}/>
                    }/>
                    <Route path='/uploader' render={()=> <Uploader  passFile= {this.getFileFromChild} fileButtonClicked={this.fileButtonClicked}/>
                    } />
                </Switch>


            </div>

        </Router>
    )
    }
}

这里是 Videos 组件,其中存储了我需要的状态:

class Videos extends React.Component{

    constructor(props){
        super(props)
        this.videoRef = React.createRef();
    }

    // once DOM loads get video tag and reload it
    componentDidUpdate(){
        this.videoRef.current.load()
    }

    render(){
        const {files, fileSelected}=this.props;
        var src = (fileSelected) ? URL.createObjectURL(fileSelected): URL.createObjectURL(files[0][0])

        return( 
                <div>
                    <div className="ui one column centered grid">
                        <div className="one wide column"> {/*needs to be one wide here not just column for center to work*/}
                            <h3>Videos</h3>

                        </div>
                    </div>
                     <div className="ui grid">
                        <div className="ten wide column">
                            <video ref={this.videoRef} controls width="566" height="320">
                                    <source src={src} id='video' type="video/mp4" />
                                    Your browser does not support HTML5 video.
                            </video>
                            <CommentsSection/>

                        </div>

                        <div className="six wide column">
                            {files[1]}
                        </div>


                    </div>

                </div>

            )


    }
}
1个回答

8
我不太明白你所说的“当视频组件加载多次时”是什么意思,但让我看看我是否能回答你的问题。
如果您的意思是在导航(更改路由)离开并返回到它时卸载和装载“视频”组件 - 导致失去您拥有的“视频”组件的状态 - 那是“Route”的“render”方法的预期行为。
让我根据React Router页面上的官方文档来解释一下:
“当您使用组件(而不是以下内容)时,路由器使用React.createElement从给定的组件创建新的React元素。这意味着,如果您提供内联函数到component属性,则会在每个呈现上创建一个新组件。这导致现有组件卸载并且新组件代替它,而不是只更新现有组件。当使用用于内联呈现的内联函数时,请使用render或childrenprop...”
这意味着:
- 传递到“渲染”或“组件”属性中的组件将始终在路由更改时卸载和安装 - 如果将内联组件传递到“渲染”属性中,则不会在每个呈现上卸载和重新安装它 - 它将“记住”内联函数并且不执行卸载/安装 - 如果将内联组件传递到“组件”属性中,则将在每个呈现上卸载并重新安装 - 它将在每个呈现上重新创建内联组件,因此它会卸载旧组件实例并挂载新创建的组件实例
总结:
- “组件”和“渲染”属性都将在导航离开时卸载,并在导航进入时安装,这是预期和所需的行为。 - 将内联组件传递到“render”属性中(例如 })) - 将组件传递到“component”属性中(例如) - 永远不要将内联组件传递到“component”属性中 - 在您的情况下,您正确地使用了“渲染”组件。您只是混淆了路由更改与prop更改的卸载和安装。

是的,那就是我的意思。但是我这里没有使用 component,而是使用 render,所以我不应该看到这种行为,对吧? - Sean Barker
3
不要混淆路由变化和属性变化时的挂载/卸载。官方文档试图说明component在属性变化时会卸载/挂载,但render不会在属性变化时卸载/挂载。两者都会在路由变化时进行卸载/挂载。在官方文档中,“每次渲染”并不是指路由变化,而是指属性变化(会触发渲染)。 - hwkd
我花了10多个小时试图以一种允许我拥有具有路由感知能力的组件且不会出现不必要状态重置的方式来重写我的应用程序,但在阅读这篇文章后,我意识到我所需要做的就是将component属性替换为render。谢谢! - Jonas Rosenqvist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接