jcolemorrison / redux-sagas-authentication-app

157 stars 56 forks source link

this doesn't work with react router v4 #1

Open sanjeevghimire opened 6 years ago

sanjeevghimire commented 6 years ago

Awesome tutorial but doesn't work with RRV4. Is it possible to modify this project to work with react router v4. this is what I am using?

{ "name": "vidhyasram_ui_v2", "version": "0.1.0", "private": true, "dependencies": { "history": "^4.6.3", "material-ui": "^0.18.6", "moment": "^2.18.1", "prop-types": "^15.5.10", "react": "^15.6.1", "react-datepicker": "^0.51.0", "react-dom": "^15.6.1", "react-redux": "^5.0.5", "react-router": "^4.1.1", "react-router-dom": "^4.1.1", "react-tap-event-plugin": "^2.0.1", "redux": "^3.7.1", "redux-form": "^6.8.0", "redux-saga": "^0.15.4" }, "devDependencies": { "react-scripts": "1.0.10" }, "scripts": { "start": "PORT=3001 react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" } }

jcolemorrison commented 6 years ago

Hey @sanjeevghimire, good question! I've had a few ask me, and not that I'm aware of at the moment. I don't currently have plans to move anything to RRv4 until they get react-router-redux up-to-speed:

https://github.com/ReactTraining/react-router/tree/master/packages/react-router-redux

And once they do, I'm not sure how it will play with Redux Sagas - I haven't had the chance to fool around with the two together yet. If you find any clear cut path to migration I would LOVE to know though.

mjoyce91 commented 6 years ago

I'm using RRv4 and was able to get this working. Still hacking around with it, so maybe I'll run into issues... but so far, I can perform login, store token to local storage, and perform auth checks on my routes and direct/redirect accordingly.

FWIW, I didn't read the tutorial. Simply looked at source code and integrated it with my existing app.

jcolemorrison commented 6 years ago

Ah nice @mjoyce91! I'd love to see more.

gperdomor commented 6 years ago

@mjoyce91 can you share the code? i'm also using RRv4

mjoyce91 commented 6 years ago

Using it in this project I'm working on. (Be sure to stay within that branch).

sanjeevghimire commented 6 years ago

I have this code: import React, {Component} from 'react'; import {connect} from 'react-redux'; import {reduxForm, Field} from 'redux-form'; import PropTypes from 'prop-types';

import { TextField } from 'redux-form-material-ui'; import Checkbox from 'material-ui/Checkbox'; import RaisedButton from 'material-ui/RaisedButton';

import loginRequest from '../../actions/loginActions';

//field validations import required from '../form-validation/required'; import email from '../form-validation/email'

class Login extends Component {

// Pass the correct proptypes in for validation static propTypes = { handleSubmit: PropTypes.func, loginRequest: PropTypes.func, pristine: PropTypes.bool, login: PropTypes.shape({ requesting: PropTypes.bool, successful: PropTypes.bool, messages: PropTypes.array, errors: PropTypes.array, }), }

componentDidMount() { this.refs.name // the Field .getRenderedComponent() // on Field, returns ReduxFormMaterialUITextField .getRenderedComponent() // on ReduxFormMaterialUITextField, returns TextField .focus(); // on TextField }

// Remember, Redux Form passes the form values to our handler // In this case it will be an object with email and password submit = (values) => { this.props.loginRequest(values) }

render (){

const { handleSubmit, pristine,
login: { requesting, successful, messages, errors, }, } = this.props

return (

); }

}

// Grab only the piece of state we need const mapStateToProps = state => ({ login: state.login, })

// make Redux state piece of login and our action loginRequest // available in this.props within our component const connected = connect(mapStateToProps, { loginRequest })(Login)

const LoginForm = reduxForm({ form: 'loginForm', })(connected)

export default LoginForm;

I am getting

screen shot 2017-08-06 at 2 11 36 pm

any help is appreciated?

sanjeevghimire commented 6 years ago

BTW what is the use of client/ in this app?

jcolemorrison commented 6 years ago

Hey @sanjeevghimire - Client is a portion of the reducer that's general to every view (vs it being a remade each time). It's there to hold general user data, authentication, settings, etc.

sanjeevghimire commented 6 years ago

@jcolemorrison Thanks for verifying. When logged in the client has token but when i refresh the page or access any other page which needs to have access to client its reset. Do you know what the problem could be?

the reason i need the client state here is to show login/register vs logout

import {connect} from 'react-redux';
import AvatarDropdown from '../components/avatarDropdown.js';
import Notification from './notification.js';
import RaisedButton from 'material-ui/RaisedButton';
import {Link, withRouter} from 'react-router-dom'

class Header extends Component {

  state = {
    logged: false,
  };

  handleChange = (event, logged) => {
    this.setState({logged: logged});
  };  

  render () {
    const style = {
      background: this.props.colorHeaderBanner.color
    }        

 console.log(this.props.client.token); // this prints false

      if (this.props.client.token){
        return (
          <header style={style} className="an-header">
            <div className="header-right">                    
                <Notification/>
                <AvatarDropdown/>
            </div>                      
          </header>            
              )
        }else{
          return (
          <header style={style} className="an-header">
            <div className="header-right">                    
              <NotLoggedHeader/>
            </div>
          </header>            
            )        
        }          
  }
}

const NotLoggedHeader = (props) => (    

    <div>  
    <Link to="/login">
        <RaisedButton label="Login" primary={true} style={style} />
    </Link>        
    <Link to="/register">
        <RaisedButton label="Register" secondary={true} style={style} />
    </Link>    
    </div>

);

const style = {
  margin: 3,
};

function mapStateToProps (state) {
  return {
    colorHeaderBanner: state.headerBAnnerActiveStyle,
    client : state.client
  }
}

export default withRouter(connect(mapStateToProps)(Header));
sanjeevghimire commented 6 years ago

@jcolemorrison this code doesn't work if i go to console and remove the token. it still thinks its authenticated. how can we handle that?

jcolemorrison commented 6 years ago

@sanjeevghimire Sorry, missed this - you're removing the token from localstorage right?

sanjeevghimire commented 6 years ago

@jcolemorrison yes. is it possible to call logout from check-auth.js if so how can we do that? and btw i am using react router 4. this is how i have done it 👍

privateRoute.js import React from 'react'; import { Redirect, Route } from 'react-router-dom'; import {isLoggedIn} from '../utils/check-auth';

  const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route {...rest} render={ props => ( 
      isLoggedIn() ? (
        <Component {...props}/>
      ) : (
        <Redirect to={{
          pathname: '/login',
          state: { from: props.location }
        }}/>
      )
      )}/>
  )

  export default PrivateRoute;

check-auth.js import jwt_decode from 'jwt-decode'; import {CLIENT_UNSET} from '../client/constants'; import

        export function isLoggedIn () {
          // attempt to grab the token from localstorage
            const storedToken = localStorage.getItem('token')
            var decoded = jwt_decode(storedToken);
            let now = new Date().getTime();
              // if it exists
            if (!storedToken) {
                logout();
                return false;
            }    

            if(now > decoded.expiration){
                logout();   
                return false;
            }

            return true
        }

        function logout() {
          return {
            type: CLIENT_UNSET
          }
        }

the problem here is logout is not working

jcolemorrison commented 6 years ago

@sanjeevghimire Thanks for sharing so much.

The problem though, is likely RRv4 (although I'm not 100% positive). RRv4 is very different from 3, and honestly haven't done any deep diving with it yet. Because all of the authentication/authorization is handled through RRv3's onEnter methods.

Additionally, from your code, it seems like you've extended it quite a bit (which is awesome). Because of this, I'm lacking some context as to why this would be failing.

BUT - in your code, logout isn't working because it has to be dispatch'ed. I don't see any sign of that code calling redux's dispatch. Therefore, the CLIENT_UNSET action isn't actually being sent through to the Saga. Instead it's just returning a plain Object. So it should be more like dispatch(logout()).

Finally, if that doesn't work, it's like because of the differences in using Redux Saga with React Router 4. There's a big thread here on it:

https://github.com/ReactTraining/react-router/issues/3972

webjunkie01 commented 6 years ago

@sanjeevghimire I'm also using RRv4 and got it working, but still haven't yet implemented the log out functionality. Here's my repo, I hope it helps.

@jcolemorrison Great tutorial dude!, got me started in the right path with react. When can we expect part 3? 😊. Keep up the good work!.

jcolemorrison commented 6 years ago

This is super helpful @webjunkie01 thanks for sharing.

For part 3 - you should be able to find it here:

https://start.jcolemorrison.com/react-and-redux-sagas-authentication-app-tutorial-part-3/

webjunkie01 commented 6 years ago

@jcolemorrison Thanks, somehow I missed the link.

dripisforever commented 6 years ago

@mjoyce91 Link is broken( tree sprint-4 isn't exist) . Is it important to use exact tree "sprint-4"?