12

I want to define a URL that could be used to logout the user (dispatch an action that would logout the user). I have not found examples showing how to implement a route dispatching an event.

Jeremy
  • 1
  • 77
  • 324
  • 346
Gajus
  • 55,791
  • 58
  • 236
  • 384
  • Why not just dispatch an action on a link click? Special route for logout is not necessary – just-boris Jan 11 '16 at 13:50
  • From the user's standpoint of view, I'd like to be able to access the logout function using an URL. – Gajus Jan 11 '16 at 14:33
  • I guess if you have Redux, that you are working on a single-page-application, that can't work with Javascript. So, what's the point to have an exposed link for it? – just-boris Jan 11 '16 at 15:22
  • 2
    Again, as a "power user", I like to type URLs to perform operations such as being able to destroy my session. Whether the app is SPA or not, has little to do with it. – Gajus Jan 11 '16 at 15:49
  • Well, just go to `/api/logout` page or something like this, that does actual logout on the backend – just-boris Jan 11 '16 at 18:01
  • 1
    The "session" persists on the client side (`window.localStorage`). Thats a standard JSON Web Token authentication implementation. Backend is not aware of user session. REST, by definition, is stateless (https://en.wikipedia.org/wiki/Representational_state_transfer#Stateless). – Gajus Jan 11 '16 at 18:42
  • Then your solution seems reasonable. [This example](https://github.com/rackt/react-router/blob/latest/examples/auth-flow/app.js#L129) in the React-router repo does the same. Redux-simple-router is a simple connector and doesn't have anything to help here. – just-boris Jan 11 '16 at 19:03

3 Answers3

11

Here is a more current implementation for such /logout page:

import { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { withRouter } from 'react-router'
import * as authActionCreators from '../actions/auth'

class LogoutPage extends Component {

  componentWillMount() {
    this.props.dispatch(authActionCreators.logout())
    this.props.router.replace('/')
  }

  render() {
    return null
  }
}
LogoutPage.propTypes = {
  dispatch: PropTypes.func.isRequired,
  router: PropTypes.object.isRequired
}

export default withRouter(connect()(LogoutPage))
Diego V
  • 5,021
  • 6
  • 34
  • 43
  • Can you explain what makes this a "more current" implementation? – Gajus Jul 09 '16 at 16:55
  • agree - I would not say this is a more current implementation but an alternative implementation using `react-router` over `redux-simple-router`.I assume this would fit most use cases using `react-router` which is more commonly used and includes the 'withRouter' functionality which was recently introduced, so why the comment. :-) – markyph Jul 20 '16 at 07:39
  • First, please note that this implementation will work both with or without redux-simple-router (which, by the way, was renamed to react-router-redux a while ago). You can use Redux-style actions if you like, but it's not needed and IMHO it's not strictly related to the original routing problem. This implementation also takes advantage of the recently introduced `withRouter` higher-order component to make it simpler :) – Diego V Jul 20 '16 at 09:30
  • withRouter should be around connect, not the other way around (to prevent screen refresh problems) – Marc Sep 14 '17 at 19:41
  • @Marc could you please elaborate? – Diego V Sep 15 '17 at 10:13
  • Sure, react-router uses global context to navigate, this means that a navigation event doesn't impact `props` and therefore, a pure component wouldn't know it has to re-render. This means that you can navigate and "nothing happens". In order to prevent this, you can use withRouter, which supplies the location prop. This prop changes in case of a navigation event, the child React component will therefore re-render. That is why it has to be around the connect call, and not the other way around. – Marc Sep 16 '17 at 20:00
9

Define a route /authentication/logout:

import React from 'react';
import {
    Route,
    IndexRoute
} from 'react-router';
import {
    HomeView,
    LoginView,
    LogoutView
} from './../views';

export default <Route path='/'>
    <IndexRoute component={HomeView} />

    <Route path='/authentication/logout'component={LogoutView} />
    <Route path='/authentication/login' component={LoginView} />
</Route>;

Create a LogoutView that dispatches an action upon componentWillMount:

import React from 'react';
import {
    authenticationActionCreator
} from './../actionCreators';
import {
    connect
} from 'react-redux';
import {
    pushPath
} from 'redux-simple-router';

let LogoutView;

LogoutView = class extends React.Component {
    componentWillMount () {
        this.props.dispatch(authenticationActionCreator.logout());
        this.props.dispatch(pushPath('/'));
    }

    render () {
        return null;
    }
};

export default connect()(LogoutView);

The componentWillMount callback dispatches two actions:

  • To destroy user session.
  • To redirect user to the IndexRoute.
this.props.dispatch(authenticationActionCreator.logout());
this.props.dispatch(pushPath('/'));
Gajus
  • 55,791
  • 58
  • 236
  • 384
  • I am not 100% sure whether thats how the logout scenario should be handled. Please suggest an edit or add your answer if this is not a canonical approach. – Gajus Jan 11 '16 at 11:12
  • It would be better to split `logout` and `pushPath` actions. `pushPath` should be in `componentDidMount` – just-boris Jan 11 '16 at 19:06
  • @just-boris What is the reason for that? – Gajus Jan 12 '16 at 13:56
  • 2
    To make things go right. Some components may also want to update and upon logout, but you are firing a route change immediately. It was only my personal point of view if you don't have issues here, that is okay to leave it as is. – just-boris Jan 12 '16 at 16:18
  • Thank you for clarification. – Gajus Jan 12 '16 at 16:33
  • Probably should redirect after upon success ( if there is any server call ). We need some types of promise. – Sisir Jul 06 '17 at 07:49
7

Here is the most current implementation for /logout page:

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";

import * as authActionCreators from "../actions/auth";

class LogoutPage extends Component {
  static propTypes = {
    dispatch: PropTypes.func.isRequired
  };

  componentWillMount() {
    this.props.dispatch(authActionCreators.logout());
  }

  render() {
    return (
      <Redirect to="/" />
    );
  }

}

export default connect()(LogoutPage);

Works for:

"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-redux": "^5.0.6",
"react-router-dom": "^4.2.2",
"prop-types": "^15.5.10",
billzhong
  • 1,306
  • 10
  • 8