Sean Barker
Sean Barker

Reputation: 351

Why is React Router causing my components to re-mount, rather than just re-rendering?

I am creating an app using react router, with several routes including a Uploader component where videos are uploaded, and a Videos component where they are viewed. The video page stores some comments as state, which i want to remain on the page throughout the app being open. However, react router seems to be causing each component to re-mount rather than re-render, causing my state to be reset to the intial empty values whenever i re-route back to the Video component. I'm using the render method rather than the component method inside my Route components, so i don't understand why this is happening. Does anyone know whats causing this?

Here is the main app where routing occurs:

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>
    )
    }
}

Here is the Videos component where the state i need is stored:

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>

            )


    }
}

Upvotes: 5

Views: 2060

Answers (1)

hwkd
hwkd

Reputation: 2648

I don't exactly get what you mean by "when the Video component is loaded more than once" but let me see if I can answer your question.

If you're saying that the Video component is unmounted and mounted when you navigate(change route) away and back to it - resulting in losing the state of the Video component you had - that is the expected behaviour of the render method of Route.

Let me explain in regards to the official docs on the react router page:

When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop...

What this means is:

  • component passed into either render or component prop will always unmount and mount on route change
  • if you pass an inline component into the render prop, it will not unmount and remount on every render - it will "remember" the inline function and not perform mount/unmount
  • if you passed an inline component into the component prop, it will unmount and mount on every render - it will re-create the inline component on every render so it performs the unmounting of the old instance of the component and mounting of the newly created instance of the component

Takeaways:

  • both component and render props will unmount on navigating away, and mount on navigating in, and that is the expected and desired behaviour.
  • you pass an inline component into render prop (e.g <Route render={() => <HomePage />})
  • you pass a component into component prop (e.g <Route component={HomePage} />)
  • never pass inline components into component prop
  • in your case, you're using the render component correctly. You just got confused with unmount and mounts on route change vs. prop change

Upvotes: 8

Related Questions