jiggles
jiggles

Reputation: 33

ComponentDidMount called multiple times

I built a HOC to use on protected routes in my app. It takes in the component that should be rendered at the route, checks if the user is authenticated, and then renders that component if they are. It works, but it causes the component to mount/unmount several times (as many times as the render function in my app.js file is called).

routes from my app.js

<Switch>
    <Route path='/groups/show/:id'
           component={ RequireAuth(Group) } />

    <Route path='/groups/create'
           component={ RequireAuth(CreateGroup) } />

    <Route path='/groups'
           component={ RequireAuth(GroupsMenu) } />

    <Route path='/tutorials/:id' component={ Tutorial } />
    <Route path='/tutorials'     component={ TutorialMenu } />
    <Route path='/ranked'        component={ RankedPlay } />
    <Route path='/casual'        component={ CasualPlay } />
    <Route path='/offline'       component={ OfflinePlay } />
    <Route path='/signup'        component={ Signup } />
    <Route path='/'              component={ Menu } />
</Switch>

require_auth.js

import React, { Component } from 'react';
import { connect }          from 'react-redux';
import { withRouter }       from 'react-router-dom';
import { store }            from '../../index';
import { AUTH_ERROR }       from '../../actions';
import PropTypes            from 'prop-types';

import Display from './display';

export default function(ComposedComponent) {
    class Authentication extends Component {
        static propTypes = {
            history: PropTypes.object.isRequired
        };

        componentWillMount() {
            const { history } = this.props;
            const error = 'You must be logged in to do this.  Please login';

            if (!this.props.authenticated) {
                store.dispatch({ type: AUTH_ERROR, payload: error });
                history.push('/');
            }
        }

        componentWillUpdate(nextProps) {
            const { history } = this.props;
            const error = 'You must be logged in to do this.  Please login';

            if (!nextProps.authenticated) {
                store.dispatch({ type: AUTH_ERROR, payload: error });
                history.push('/');
            }
        }

        render() {
            return (
                <Display if={ this.props.authenticated } >
                    <ComposedComponent { ...this.props } />
                </Display>
            );
        }
    }

    function mapStateToProps(state) {
        return {
            authenticated: state.auth.authenticated
        };
    }

    return withRouter(connect(mapStateToProps)(Authentication));
}

If you remove RequireAuth() from any of the routes, the component only mounts once when you hit the route. But adding it causes the component to mount every time app.js render() fires. Is there a way I can set this up so the component only mounts once?

Upvotes: 3

Views: 7224

Answers (1)

cfraser
cfraser

Reputation: 961

By calling RequireAuth(Component) in render, you are decorating Component with your HOC in every render call, making that each render returns a new Component each render.

You should decorate Group, CreateGroup and GroupsMenu with RequireAuth, before exporting them. Just as you would with react-redux's connect.

Upvotes: 9

Related Questions