Open sanjeevghimire opened 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.
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.
Ah nice @mjoyce91! I'd love to see more.
@mjoyce91 can you share the code? i'm also using RRv4
Using it in this project I'm working on. (Be sure to stay within that branch).
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
any help is appreciated?
BTW what is the use of client/ in this app?
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.
@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));
@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?
@sanjeevghimire Sorry, missed this - you're removing the token from localstorage right?
@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
@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:
@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!.
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/
@jcolemorrison Thanks, somehow I missed the link.
@mjoyce91 Link is broken( tree sprint-4 isn't exist) . Is it important to use exact tree "sprint-4"?
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" } }