skip to Main Content

I am trying to implement Twitter authentication with React/Redux/Node.js. The back-end part is working fine, since the front-end is getting the right data.
The problem I’m facing right now is at the front-end. More specifically, when the token is received after the successful authentication, an action is dispatched, so that the props are updated as a consequence. However, this doesn’t happen. Here is the code:

Header.js

import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import TwitterLogin from 'react-twitter-auth';
import { connect } from 'react-redux';
import { loginSuccess, logout } from './redux/actions/actions';

import 'bulma/css/bulma.css';

class Header extends Component {

  constructor(props) {
    super(props);

    this.onSuccess = this.onSuccess.bind(this);
    this.onFailed = this.onFailed.bind(this);
  }

  onSuccess = (response) => {
    const token = response.headers.get('x-auth-token');
    response.json().then(user => {
      if (token) {
        this.props.loginSuccess(user, token);
      }
    });
  };

  onFailed = (error) => {
    alert(error);
  };

  logout = () => {
    this.props.logout();
  };

  render() {
    let content = this.props.isAuthenticated === true ?
      (
        <div>
          <div>
            <Link className="button is-primary" to="/profile">Profile</Link>
            <button onClick={this.logout} className="button" >
              Log out
            </button>
          </div>
        </div>
      ) :
      (
        <TwitterLogin 
          loginUrl="http://localhost:5000/api/auth/twitter"
          onFailure={this.onFailed} onSuccess={this.onSuccess}
          requestTokenUrl="http://localhost:5000/api/auth/twitter/reverse"
          className="button" />
      );


    return (
      <header className="navbar has-shadow is-spaced">
        <div className="container">
          <div className="navbar-brand">
            <h1 className="title is-4">
              <Link to="/" className="navbar-item">
                <strong>App</strong>
              </Link>
            </h1>
          </div>
          <div className="navbar-end">
            <div className="buttons">
              {content}
            </div>
          </div>
        </div>
      </header>
    );
  }
}

const mapStateToProps = ({token, user, isAuthenticated}) => ({token, user, isAuthenticated});

export default connect(
  mapStateToProps,
  {loginSuccess,
  logout})(Header);

actions.js

export const loginSuccess = (user, token) => {
  return (dispatch) => {
    dispatch({type: 'LOGIN_SUCCESS', payload: {user, token}})
  }
}

reducers.js

const initialState = {
  token: null,
  user: null,
  isAuthenticated: false,
  error: false
};

export default (state=initialState, action) => {
  switch (action.type) {
    case 'LOGOUT':
      localStorage.removeItem('user');
      return initialState;
    case 'LOGIN_ERROR':
      return {
        ...state,
        error: true
      }
    case 'LOGIN_SUCCESS':
      return {
        token: action.payload.token,
        user: action.payload.user,
        isAuthenticated: true,
        error: false
      }
    default:
      return state;
  }
}

store.js

import {applyMiddleware, createStore} from 'redux';
import thunkMiddleware from 'redux-thunk';
import reducer from './reducer';

const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);

export const store = createStoreWithMiddleware(reducer);

What am I missing here? Is there something missing in the onSuccess function?
Thanks!

2

Answers


  1. Chosen as BEST ANSWER

    I found the issue! I was declaring the mapStateToProps in the wrong way.
    The right code is the following:

    Header.js

    import React, { Component } from 'react';
    import { Link } from 'react-router-dom';
    import TwitterLogin from 'react-twitter-auth';
    import { connect } from 'react-redux';
    import { loginSuccess, logout } from './redux/actions/actions';
    
    import 'bulma/css/bulma.css';
    
    class Header extends Component {
    
      constructor(props) {
        super(props);
    
        this.onSuccess = this.onSuccess.bind(this);
        this.onFailed = this.onFailed.bind(this);
      }
    
      onSuccess = (response) => {
        const token = response.headers.get('x-auth-token');
        response.json().then(user => {
          if (token) {
            this.props.loginSuccess(user, token);
          }
        });
      };
    
      onFailed = (error) => {
        alert(error);
      };
    
      logout = () => {
        this.props.logout();
      };
    
      render() {
        let content = this.props.users.isAuthenticated === true ?
          (
            <div>
              <div>
                <Link className="button is-primary" to="/profile">Profile</Link>
                <button onClick={this.logout} className="button" >
                  Log out
                </button>
              </div>
            </div>
          ) :
          (
            <TwitterLogin 
              loginUrl="http://localhost:5000/api/auth/twitter"
              onFailure={this.onFailed} onSuccess={this.onSuccess}
              requestTokenUrl="http://localhost:5000/api/auth/twitter/reverse"
              className="button" />
          );
    
    
        return (
          <header className="navbar has-shadow is-spaced">
            <div className="container">
              <div className="navbar-brand">
                <h1 className="title is-4">
                  <Link to="/" className="navbar-item">
                    <strong>App</strong>
                  </Link>
                </h1>
              </div>
              <div className="navbar-end">
                <div className="buttons">
                  {content}
                </div>
              </div>
            </div>
          </header>
        );
      }
    }
    
    const mapStateToProps = ({users}) => ({users});
    
    export default connect(
      mapStateToProps,
      {loginSuccess,
      logout})(Header);
    

    Here is the code where I combine the reducers:

    reducer.js

    import { combineReducers } from 'redux';
    import users from './reducers/users';
    import polls from './reducers/polls';
    
    export default combineReducers({
        users,
        polls
    });
    

    By using this code, I'm able to see the props updated after the action is dispatched.
    Thanks for your help!


  2. Please try to replace

        case 'LOGIN_SUCCESS':
      return {
        token: action.payload.token,
        user: action.payload.user,
        isAuthenticated: true,
        error: false
      }
    

    by:

        case 'LOGIN_SUCCESS':
      return {
        ...state,token: action.payload.token,
        user: action.payload.user,
        isAuthenticated: true,
        error: false
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search