javascriptreactjsmeteorbrowser-historymeteor-useraccounts

Meteor/React: Redirect outside of component as callback of AccountsTemplates.logout()


I'm trying to integrate the meteor-useraccounts package with React in my Meteor app. I've come pretty far, but am struggling to implement a history.push() or equivalent as a callback function to the AccountsTemplate.logout() - the latter being the built in way of logging out a user.

The configuration of the callback func is done this way:

AccountsTemplates.configure({
    onLogoutHook: myLogoutFunc
})

But I can't do this within a "class" of React.Component. So I need to define callback function that uses the browser history, but is outside of any Components scope, I guess.

For the case of hitting a Sign In/Out button, I found a solution as follows:

class LogInOutButton extends Component {
    constructor(props) {
        super(props);

        this.state = {
            loggedIn: Meteor.userId() ? true : false,
            redirect: false
        }

        this.redirectToSignIn = this.redirectToSignIn.bind(this);

        window.logInOutButton = this;
    }

    redirectToSignIn() {
        if (!this.state.loggedIn && document.location.pathname !== "/signIn")
            this.setState({redirect: <Redirect to="/signIn" />});
    }

    render() {
        const { redirect } = this.state
        return (
            <li className="nav-item">
                {redirect}
                <a className="nav-link" href="#"
                    onClick={Meteor.userId() ? AccountsTemplates.logout : this.redirectToSignIn}>
                    {Meteor.userId() ? "Sign Out" : "Sign In"}
                </a>
            </li>
        )
    }
}

As you can see I, for example, tried calling the redirectToSignIn() method from outside by making it a member of the global window object. Doesn't feel right and doesn't work either:

var myLogoutFunc = () =>
    window.logInOutButton.redirectToSignIn();

I also tried the following approach:

var myLogoutFunc = withRouter(({history}) => 
    <div onLoad={history.push="/signIn"}>
    </div>
) 

However, this won't work, because withRouter needs a Component as param, if I got it right? At least this is how I try to explain the following error:

Exception in delivering result of invoking 'logout': TypeError: "props is undefined"

After reading this in the withRouter module:

var withRouter = function withRouter(Component) {
  var C = function C(props) {
     ...

How would you solve this?


Solution

  • Are you just trying to redirect the user to a landing page when they log out, are logged out or when they come to the app/site while not logged in? I often do this a few different ways depending on the complexity of my app/routes/permissions/etc.

    I tend to use iron:router and Blaze, not React, but I think the most straightforward solution is easy to translate. I drop this somewhere at the top level of my client-side code:

    Tracker.autorun(function() {
        const userDoc = Meteor.user();
        const loggingIn = Meteor.loggingIn();
    
        if(!userDoc && !loggingIn) {
            Router.go('/'); // Or comparable redirect command
        }
    });
    

    On page load: If the userDoc is not defined (meaning the user is not logged in successfully) and loggingIn is not true (meaning Meteor is not in the process of logging a user in on page load), we'll redirect the user to the home page.

    On log out: Since userDoc and loggingIn are both reactive data sources, this autorun function will rerun any time either of those variables change. So, when the user logs out by any means, userDoc will cease to be defined, the conditional will resolve to true and it'll redirect the user to the home page.

    On log in: If a logged-out user logs in or begins to log in, nothing will happen. The function will re-run, but forcing an automatic redirect any time the userDoc or loggingIn variable change can become more complicated.