State not updating in application - javascript

I am trying to get my application to update the state on my header once I sign in. It was initially doing so but stopped after creating a few sample users. I am not sure whether that is a problem with my Firestore settings or some code in the app is causing this, but it is quite frustrating. This error also prevents me from signing out any users, thus the statement below in my App.js file keeps outputting the last user that signed in, even after refreshing the page.
console.log('THE USER IS >>> ', authUser)
fbConfig.jsx file
import { initializeApp } from 'firebase/app'
import { getFirestore } from 'firebase/firestore/lite'
import { getAuth } from 'firebase/auth'
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: 'xxxx',
authDomain: 'xxxx',
projectId: 'xxxx',
storageBucket: 'xxxx',
messagingSenderId: 'xxxx',
appId: 'xxxx',
measurementId: 'xxxx'
}
//Init firebase app
const firebaseApp = initializeApp(firebaseConfig)
//init services
const db = getFirestore(firebaseApp)
const auth = getAuth(firebaseApp)
export { auth, db }
Login.jsx
import React, { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { auth } from '../../fbConfig'
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword
} from 'firebase/auth'
import { icon } from '../../constants'
import './Login.css'
const Login = () => {
const navigate = useNavigate()
const { email, setEmail } = useState('')
const { password, setPassword } = useState('')
const signIn = (e) => {
e.preventDefault()
signInWithEmailAndPassword(auth, email, password)
.then((auth) => {
// Signed in
const user = auth.user
console.log('success' + user + 'registered')
navigate('/', { replace: true })
// ...
})
.catch((error) => {
alert(error.message)
})
}
const register = (e) => {
e.preventDefault()
createUserWithEmailAndPassword(auth, email, password)
.then((auth) => {
// Signed in
const user = auth.user
console.log('success' + user + 'registered')
// ...
})
.catch((error) => {
alert(error.message)
// const errorCode = error.code
// const errorMessage = error.message
})
}
return (
<div className='login'>
<div className='login__mainContainer'>
<Link to='/'>
<img className='login__logo' src={icon} alt='Webiste logo' />
</Link>
<div className='login__container'>
<h1>Sign-in</h1>
<form>
<div className='login__input'>
<h5>E-mail</h5>
<input
className='login__inputEmail'
type='text'
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<span className='login__inputSpan'>* Required</span>
</div>
<div className='login__input'>
<h5>Password</h5>
<input
className='login__inputPassword'
type='password'
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<span className='login__inputSpan'>* Required</span>
</div>
<button
type='submit'
className='login__signInButton'
onClick={signIn}
>
Sign In
</button>
</form>
<button className='login__registerButton' onClick={register}>
Create your account
</button>
</div>
</div>
</div>
)
}
export default Login
Reducer.jsx file
export const initialState = {
basket: [],
user: null
}
// Selector
export const getBasketTotal = (basket) =>
basket?.reduce((amount, item) => item.price + amount, 0)
const reducer = (state, action) => {
console.log(action)
switch (action.type) {
case 'ADD_TO_BASKET':
return {
...state,
basket: [...state.basket, action.item]
}
case 'SET_USER':
return {
...state,
user: action.user
}
default:
return state
}
}
export default reducer
App.js
import { useEffect } from 'react'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { Header, Home, Login } from './components'
import { auth } from './fbConfig'
import { useStateValue } from './contextAPI/StateProvider'
import './App.css'
function App() {
const [dispatch] = useStateValue()
useEffect(() => {
auth.onAuthStateChanged((authUser) => {
console.log('THE USER IS >>> ', authUser)
if (authUser) {
// the user just logged in / the user was logged in
dispatch({
type: 'SET_USER',
user: authUser
})
} else {
// the user is logged out
dispatch({
type: 'SET_USER',
user: null
})
}
})
})
return (
<Router>
<div className='app'>
<Header />
<Routes>
<Route path='/login' element={<Login />} />
<Route path='/' element={<Home />} />
</Routes>
</div>
</Router>
)
}

Related

While I enter data through a form and submit , firebase gets the image but details of other data didn't get on the firestore collections

Image can be seen on the storage of firebase .Error is Cannot read properties of undefined (reading 'name') at submitPortfolio .But it fails to add data.Once a error in the line const storageRef = ref(storage, `portfolio/${image.name}`); seems .But now it didn't.I don't know why!
firebase.js
import { initializeApp } from "firebase/app";
import { getAuth, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import { getStorage } from 'firebase/storage';
import { getFirestore } from 'firebase/firestore/lite';
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: ",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
const app = initializeApp(firebaseConfig);
export const auth = getAuth();
const provider = new GoogleAuthProvider();
export const db = getFirestore(app);
export const storage = getStorage(app);
export const signInWithGoogle = () => signInWithPopup(auth, provider);
login.js
import { signInWithGoogle } from '../../firebase';
import React from 'react'
const Login = () => {
return (
<div className="dashboard">
<button onClick={signInWithGoogle}>
Sign in with google
</button>
</div>
)
}
export default Login;
index.js
import { useEffect, useState } from "react";
import { getAuth, onAuthStateChanged } from 'firebase/auth';
import Home from "./home";
import Login from '../Login';
import React from 'react';
const Dashboard = () => {
const [user, setUser] = useState(null);
const auth = getAuth();
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if(user) {
setUser(user);
} else {
setUser(null);
}
})
}, []);
return (
<div>
{user ? <Home /> : <Login />}
</div>
)
}
export default Dashboard;
Home.js
import { useRef } from 'react';
import { auth, storage, db } from '../../firebase';
import { ref, uploadBytes, getDownloadURL } from 'firebase/storage';
import { addDoc } from 'firebase/firestore';
import { collection } from 'firebase/firestore/lite';
const Home = () => {
const form = useRef();
const submitPortfolio = (e) => {
e.preventDefault();
const name = form.current[0]?.value;
const description = form.current[1]?.value;
const url = form.current[2]?.value;
const image = form.current[3]?.files[0];
const storageRef = ref(storage, `portfolio/${image.name}`);
uploadBytes(storageRef, image).then(
(snapshot) => {
getDownloadURL(snapshot.ref).then((downloadUrl) => {
savePortfolio({
name,
description,
url,
image: downloadUrl
})
}, (error) => {
console.log(error);
savePortfolio({
name,
description,
url,
image: null
})
})
}, (error) => {
console.log(error);
savePortfolio({
name,
description,
url,
image: null
})
}
)
}
const savePortfolio = async (portfolio) => {
console.log(portfolio)
try {
await addDoc(collection(db, 'portfolio'), portfolio);
window.location.reload(false);
} catch (error) {
alert('Failed to add portfolio');
}
}
return (
<div className="dashboard">
<form ref={form} onSubmit={submitPortfolio}>
<p><input type="text" placeholder="Name" /></p>
<p><textarea placeholder="Description" /></p>
<p><input type="text" placeholder="Url" /></p>
<p><input type="file" placeholder="Image" /></p>
<button type="submit">Submit</button>
<button onClick={() => auth.signOut()}>Sign out</button>
</form>
</div>
)
}
export default Home;
I think you are using the wrong variable for the storageRef. You declared the variable name but use image.name
Change in submitProtfiolio the storageRef to:
const storageRef = ref(storage, `portfolio/${name}`);

Context is cleared when I navigate to another page with the browser address bar

The user's data is received from the API on the login page and set in the user context, and after redirecting to the AdminPanelApp, our data that is inside the user context is displayed correctly.
But when I am on the AdminPanelApp and while my context contains user information, I go to the loginpage through the address bar, all the context user information is deleted.
How can I fix the problem so that the context information is not deleted
Picture of my app:
App.js:
import React, { useState , useEffect } from "react";
import { Routes, Route } from "react-router-dom";
import Home from './Components/Home';
import LoginPage from './Components/LoginPage';
import AdminPanelApp from './Components/Dashboard/AdminPanelApp';
//Context
import UserContext from "./Context/user";
function App(){
const [ userinfo , setuserinfo ] = useState({
userid : '' ,
user_name : '' ,
email : '' ,
token : '' ,
});
return(
<UserContext.Provider value = {
{
userinfo : userinfo ,
setuserinfo
}
}>
<Routes>
<Route path="/" element= { <LoginPage /> } />
<Route path="/Login" element= { <LoginPage /> } />
<Route path="/admin-panel" element= { <AdminPanelApp /> } />
</Routes>
</UserContext.Provider>
);
}
export default App;
LoginPage.js:
import React , { useState , useEffect , useContext } from "react";
import { useNavigate } from "react-router-dom";
import UserContext from '../Context/user';
import '../assets/css/LoginPage.css';
import axios from "axios";
function LoginPage () {
let navigate = useNavigate();
const userContext = useContext(UserContext);
let { userinfo } = userContext;
const [ UserNameInput , setUserNameInput ] = useState('');
const [ PasswordInput , setPasswordInput ] = useState('');
const UserNameInputHandler = (e) => {
setUserNameInput(e.target.value)
}
const PasswordInputHandler = (e) => {
setPasswordInput(e.target.value)
}
let FormHandler = (e)=> {
e.preventDefault();
axios.post(
`http://127.0.0.1:8000/api/login`,
{
email : UserNameInput ,
password : PasswordInput
}
).then( response => {
if( response.data.token ) {
userContext.setuserinfo({
userid : response.data.userid ,
user_name : response.data.user_name ,
email : response.data.user_email ,
token : response.data.token ,
})
navigate('/admin-panel');
}
}
)
.catch( err => {
console.log(err);
})
}
return (
<>
<main>
<form className="row g-3 needs-validation" method="post">
<div className="col-12">
<div className="input-group has-validation">
<span className="input-group-text" id="inputGroupPrepend">#</span>
<input type="text" name="username" onChange={ UserNameInputHandler } className="form-control " id="yourUsername" required/>
</div>
<div className="col-12">
<input type="password" name="password" onChange={ PasswordInputHandler } className="form-control " id="yourPassword" required/>
</div>
<div className="col-12">
<button className="btn btn-primary w-100" onClick={(e) => FormHandler(e) } type="submit">submit</button>
</div>
</form>
</main>
</>
)
}
export default LoginPage;
AdminPage.js:
import React , { useEffect , useContext } from "react";
import { useNavigate } from "react-router-dom";
import UserContext from '../../Context/user';
import '../../assets/css/style.css';
import Header from './Layouts/Header';
import Sidebar from './Layouts/Sidebar';
import Footer from './Layouts/Footer';
import Dashboard from "./Dashboard";
// import axios from "axios";
function AdminPanelApp () {
let navigate = useNavigate();
const userContext = useContext(UserContext);
let { userinfo } = userContext;
if (userinfo.token){
return (
<>
<h1>{ user.state.token } </h1>
</>
)
}
else{
navigate('/Login');
}
}
export default AdminPanelApp;
UserContext.js:
import React from "react";
const UserContext = React.createContext({
userinfo : {} ,
setUser : () => { } ,
})
export default UserContext;
Navigating with browser's address bar refreshes the page, while navigate does not. And after a page refresh everything come to its initial value. Your context would take that initial object given to that useState in App which is:
{
userid: "",
user_name: "",
email: "",
token: "",
}
If you don't wanna loose data accros page refreshes (different visites), you should use for example localStorage. For that you can first change FormHandler to:
let FormHandler = (e) => {
e.preventDefault();
axios
.post(`http://127.0.0.1:8000/api/login`, {
email: UserNameInput,
password: PasswordInput,
})
.then((response) => {
if (response.data.token) {
let user = {
userid: response.data.userid,
user_name: response.data.user_name,
email: response.data.user_email,
token: response.data.token,
};
userContext.setuserinfo(user);
localStorage.setItem("user", JSON.stringify(user));
navigate("/admin-panel");
}
})
.catch((err) => {
console.log(err);
});
};
And change that state definition inside App to this:
const [userinfo, setuserinfo] = useState(
!localStorage.getItem("user")
? {
userid: "",
user_name: "",
email: "",
token: "",
}
: JSON.parse(localStorage.getItem("user"))
);
Data regarding user logins should be handled from the backend using sessions. Example with Express Js backend
But if you need to:
You are already using react router dom, if you wish to move from pages you can use the <Link> Tags which won't refresh your page, but if you update the address bar, it will refresh the page causing you to lose your context data.
And also regarding your question on using local storage, it is not safe to use local storage for any kind of user logins as this data is not encrypted.

Firebase createUserProfileDocument() is returning undefined

the createUserProfileDocument() method from firebase is returning undefined for some reason, therefore, it's throwing an error on my broswer's console.
The error thrown is:
App.js:23 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'onSnapshot') at App.js:23:1
the code:
import React from 'react';
import './App.css';
import { connect } from 'react-redux';
import { Switch, Route } from 'react-router-dom';
import HomePage from './pages/homepage/homepage.component';
import ShopPage from './pages/shop/shop.component';
import Header from './components/header/header.componet';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';
import SignInAndSignUpPage from './pages/sign-in-and-sign-up/sign-in-and-sign-up.component';
import { setCurrentUser } from './redux/user/user.actions';
class App extends React.Component {
unsubscribeFromAuth = null;
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => {
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
}
componentWillUnmount() {
this.unsubscribeFromAuth();
}
render() {
return (
<div className='App'>
<Header />
<Switch>
<Route exact path='/' component={HomePage} />
<Route exact path='/shop' component={ShopPage} />
<Route
exact
path='/signin'
component={SignInAndSignUpPage}
/>
</Switch>
</div>
);
}
}
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});
export default connect(null, mapDispatchToProps)(App);
The portion of code related to my error is the following:
componentDidMount() {
const { setCurrentUser } = this.props;
this.unsubscribeFromAuth = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => { // This is Line 23......
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
}
I console.logged(userRef) as you can see, and it returns undefined. This is my first experience with firebase so I am not sure where the error is.
I googled and found an answer here in stackoverflow which is alike to my issue: TypeError: Cannot read properties of undefined (reading 'onSnapshot')
However, that problem is specifically related to how the developer was passing the method as an array and not a method, which does not help my situation.
EDIT: Adding firebase.utils code.
import firebase from 'firebase/compat/app';
import 'firebase/compat/firestore';
import 'firebase/compat/auth';
const config = {
apiKey: 'AIzaSyDxUMY8sUpOMAoxEyHo41ONilzjL2woWHk',
authDomain: 'crown-db-d4416.firebaseapp.com',
projectId: 'crown-db-d4416',
storageBucket: 'crown-db-d4416.appspot.com',
messagingSenderId: '887003208895',
appId: '1:887003208895:web:778f8f55a0ad2c72953a0a',
measurementId: 'G-6TGCRVN7D3',
};
export const createUserProfileDocument = async (userAuth, additionalData) => {
if (!userAuth) return;
const userRef = firestore.doc(`users/${userAuth.uid}`);
const snapShot = await userRef.get();
console.log(snapShot);
if (!snapShot.exists) {
const { displayName, email } = userAuth;
const createdAt = new Date();
try {
await userRef.set({
displayName,
email,
createdAt,
...additionalData,
});
} catch (error) {
console.log('Error creating user: ', error.message);
}
}
};
firebase.initializeApp(config);
export const auth = firebase.auth();
export const firestore = firebase.firestore();
const googleProvider = new firebase.auth.GoogleAuthProvider();
export const signInWithGoogle = () => {
auth
.signInWithPopup(googleProvider)
.then((res) => {
console.log(res.user);
})
.catch((error) => {
console.log(error.message);
});
};
export default firebase;
Your createUserProfileDocument function doesn't return anything, so that explains why this code then fails:
const userRef = await createUserProfileDocument(userAuth);
console.log(userRef);
userRef.onSnapshot((snapshot) => {
The solution is to return the userRef from createUserProfileDocument as your calling code expects:
export const createUserProfileDocument = async (userAuth, additionalData) => {
...
return userRef;
};

in React, "history.push('/')" is not working in some component

I'm trying to make re-directing function from the QnaNew.js.
onSubmit = (formProps) => {
this.props.createPost(formProps, () => {
this.props.history.push('/qna');
});
};
I want to make it When I send post req then redirect to '/qna'.
Strangely, the redirect is working in Login.js, Join.js page.
history.js
import { createBrowserHistory } from 'history';
export default createBrowserHistory();
src - index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router-dom';
import './index.css';
import App from './App';
import history from './history';
// Redux
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import reducers from './reducers';
import reduxThunk from 'redux-thunk';
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducers,
{
auth: { authenticated: localStorage.getItem('token') },
},
composeEnhancer(applyMiddleware(reduxThunk))
);
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<App />
</Router>
</Provider>,
document.getElementById('root')
);
QnANew.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { CKEditor } from '#ckeditor/ckeditor5-react';
import ClassicEditor from '#ckeditor/ckeditor5-build-classic';
import { compose } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../../actions';
//import { createPost } from '../../actions'
class QnANew extends Component {
renderInput({ input, label, meta }) {
return (
<div className="qnaTitle">
<label>{label}</label>
<input {...input} />
<div>{meta.error}</div>
</div>
);
}
renderEditor({ input, label }) {
return (
<div className="qnaContent">
<label>{label}</label>
<CKEditor
data={input.value}
editor={ClassicEditor}
onChange={(event, editor) => {
return input.onChange(editor.getData());
}}
/>
</div>
);
}
onSubmit = (formProps) => {
this.props.createPost(formProps, () => {
this.props.history.push('/qna');
});
};
render() {
const { handleSubmit } = this.props;
return (
<div className="QnaNew">
<h2>Using CKEditor 5 build in React</h2>
<h2>Using CKEditor 5 build in React</h2>
<h2>Using CKEditor 5 build in React</h2>
<h2>Using CKEditor 5 build in React</h2>
<h2>Using CKEditor 5 build in React</h2>
<form onSubmit={handleSubmit(this.onSubmit)} className="ui form">
<fieldset>
<Field name="title" type="text" component={this.renderInput} label="Enter Title" autoComplete="none" />
</fieldset>
<fieldset>
<Field name="content" type="text" component={this.renderEditor} label="Enter Description" autoComplete="none" />
</fieldset>
<button type="submit">Submit</button>
</form>
</div>
);
}
}
const validate = (formValues) => {
const errors = {};
if (!formValues.title) {
errors.title = 'You must enter a title';
}
return errors;
}
const formWrapped = reduxForm({ form: 'QnANew', validate })(QnANew);
export default compose(connect(null, actions))(formWrapped);
actions - index.js
import axios from 'axios';
import history from '../history';
import qna from '../apis/qna';
import { AUTH_USER, AUTH_ERROR, CREATE_QNA, FETCH_QNA, FETCH_QNAS, EDIT_QNA, DELETE_QNA } from './types';
export const join = (formProps, callback) => async dispatch => {
try {
const response = await axios.post('http://localhost:3001/join', formProps);
dispatch({ type: AUTH_USER, payload: response.data.token });
localStorage.setItem('token', response.data.token);
callback();
} catch (e) {
dispatch({ type: AUTH_ERROR, payload: 'Email in use'});
}
}
export const login = (formProps) => async (dispatch) => {
try {
const response = await axios.post('http://localhost:3001/login', formProps);
dispatch({ type: AUTH_USER, payload: response.data.token });
localStorage.setItem('token', response.data.token);
history.push('/');
} catch (e) {
dispatch({ type: AUTH_ERROR, payload: 'Invalid login credentials' });
}
};
export const logout = () => {
localStorage.removeItem('token');
return {
type: AUTH_USER,
payload: ''
};
}
export const createPost = (formProps) => async (dispatch) => {
const response = await qna.post('/qnanew', { ...formProps });
dispatch({ type: CREATE_QNA, payload: response.data });
history.push('/qna');
};
export const fetchPost = (id) => async dispatch => {
const response = await qna.get(`/qna/${id}`);
dispatch({ type: FETCH_QNA, payload: response.data });
};
// Post List
export const fetchPosts = () => async dispatch => {
const response = await qna.get('/qna');
dispatch({ type: FETCH_QNAS, payload: response.data });
};
export const editPost = (id, formValues) => async dispatch => {
const response = await qna.put(`/qna/${id}`, formValues);
dispatch({ type: EDIT_QNA, payload: response.data });
};
export const deletePost = (id) => async dispatch => {
await qna.delete(`/qna/${id}`);
dispatch({ type: DELETE_QNA, payload: id });
}
Login.js
import React, { Component } from 'react';
import { reduxForm, Field } from 'redux-form';
import { compose } from 'redux';
import { connect } from 'react-redux';
import * as actions from '../actions';
import './Login.scss';
class Login extends Component {
onSubmit = (formProps) => {
this.props.login(formProps, () => {
this.props.history.push('/');
});
};
render() {
const { handleSubmit } = this.props;
return (
<div className="Join">
<h1>Login</h1>
<p>Login page</p>
<form onSubmit={handleSubmit(this.onSubmit)} className="JoinForm">
<fieldset>
<label>Email</label>
<Field name="email" type="text" component="input" autoComplete="none" />
</fieldset>
<fieldset>
<label>Password</label>
<Field name="password" type="password" component="input" autoComplete="none" />
</fieldset>
<div>{this.props.errorMessage}</div>
<button type="submit">Login</button>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return { errorMessage: state.auth.errorMessage };
}
export default compose(connect(mapStateToProps, actions), reduxForm({ form: 'login' }))(Login);
you can use like this:
this.props.history.push('/');
this.props.history.go('/');
or:
window.location.href = "/";

Redux and Token Authentication : how to redirect the user

I made my own Django rest Framework API and I included token authentication. Everything works fine, I can get tokens and stuff, BUT, I have some trouble redirecting my user, using :
this.props.push('/');
Here is the Logic behind the authentication for the UI :
import React, {Component} from 'react';
import { connect } from 'react-redux';
import {authLogin} from '../../actions/authentication.js';
class Login extends Component {
constructor(props){
super(props);
this.state = {
email: "",
password: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e){
this.setState({[e.target.name]: e.target.value});
}
handleSubmit(e){
e.preventDefault();
this.props.authLogin(this.state.email, this.state.password);
console.log(this.props.isAuthenticated);
console.log(this.props.loading);
if (this.props.isAuthenticated){
this.props.history.push('/');
}
}
render(){
return(
<div className="Login">
<h1> This is a form </h1>
<form>
<input type="text" onChange={this.handleChange} name="email"/>
<input type="password" onChange={this.handleChange} name="password"/>
<input type="submit" onClick={this.handleSubmit}/>
{ this.props.isAuthenticated &&
<p>Hello </p>
}
</form>
</div>
)
}
}
const mapStateToProps = state => {
console.log(state);
return {
token: state.authentication.token
}
}
export default connect(mapStateToProps, {authLogin})(Login);
This Login Component is inside a Container Component and this is where I want my Redux to tell " hey, here is the new token " :
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import {connect } from 'react-redux';
import AppHeader from './header/AppHeader';
import HomePageStream from './HomePageStream/HomePageStream.js';
import HeaderCategories from './header/HeaderCategories.js';
import ArticleDetails from './ArticleDetails/ArticleDetails.js';
import { Route, Switch } from 'react-router-dom';
import { transitions, positions, Provider as AlertProvider } from 'react-alert'
import AlertTemplate from 'react-alert-template-basic'
import Alerts from './Alerts/Alerts.js';
import {authStateCheck} from '../actions/authentication.js';
import { Provider } from 'react-redux'
import { store } from '../store.js';
import { BrowserRouter } from "react-router-dom";
import Login from './Authentication/Login.js';
const options = {
timeout: 2000,
position: "top center"
}
class Container extends Component {
componentDidMount(){
this.props.authStateCheck();
}
render(){
return(
<div className="Container">
<Provider store={store}>
<AlertProvider template={AlertTemplate}
{...options}
>
<AppHeader />
<Alerts />
<HeaderCategories />
<Switch>
<Route exact
path="/:category"
render={routerProps => <HomePageStream {...routerProps}/>}
/>
<Route exact
path="/article/:slug"
render={routerProps => <ArticleDetails {...routerProps}/>}
/>
<Route exact
path="/authentication/login"
render={ routerProps => <Login {...routerProps}{...this.props}/>}
/>
</Switch>
</AlertProvider>
</Provider >
</div>
)
}
}
const mapStateToProps = state => {
console.log(state.authentication);
return {
isAuthenticated: state.authentication.token !== null,
loading: state.authentication.loading
}
}
export default connect(mapStateToProps, {authStateCheck} )(Container);
Here is the Problem :
When I click on submit, isAuthenticated is false, because I only get the token AFTER handleSubmit() has been called !
Here is the code for my action :
import axios from 'axios';
import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from './type';
export const authStart = () => {
return {
type: AUTH_START,
loading: true
};
}
export const authSucess = token => {
console.log(token);
return {
type: AUTH_SUCESS,
token: token,
error: null,
loading: false
};
}
export const authFail = error => {
console.log(error)
return {
token: null,
type: AUTH_FAIL,
error: error,
loading: false
};
}
export const logout = () => {
window.localStorage.removeItem('token');
window.localStorage.removeItem('expiration_time');
return {
type: AUTH_LOGOUT
}
}
export const checkAuthTimeOut = expiration_time => dispatch => {
return setTimeout(()=> {
dispatch(logout());
}, expiration_time * 1000);
}
export const authLogin = (email, password) => dispatch => {
dispatch(authStart());
console.log("I'm authlogin ! ");
axios.post('http://127.0.0.1:8000/rest-auth/login/',{
"email": email,
"password": password
})
.then( res => {
console.log("RESPONSE !")
const token = res.data.key
const expiration_time = new Date(new Date().getTime() + 3600 * 1000);
window.localStorage.setItem('token', token);
window.localStorage.setItem('expiration_time', expiration_time);
dispatch(authSucess(token));
console.log(token);
dispatch(checkAuthTimeOut(3600));
})
.catch( err => {
console.log(err);
dispatch(authFail());
})
}
export const authStateCheck = () => dispatch => {
const expiration_time = window.localStorage.getItem('expiration_time');
const token = window.localStorage.getItem('token');
if (!token){
return dispatch(logout());
} else if ( expiration_time <= new Date()){
console.log("EXPIRATION");
console.log( expiration_time <= new Date() )
return dispatch(logout());
} else {
console.log("NEW TIMER !!!! ");
return checkAuthTimeOut((expiration_time - new Date().getTime()) / 1000)
}
}
Here is my reducer :
import {AUTH_START, AUTH_FAIL, AUTH_SUCESS, AUTH_LOGOUT} from '../../actions/type';
const initialState = {
error: null,
token: null,
loading: false
}
export function authenticationReducer(state = initialState, action){
switch (action.type) {
case AUTH_START:
return {
...state,
loading: true
}
case AUTH_SUCESS:
console.log(action.token);
console.log(action.loading);
return {
...state,
error: null,
token: action.token,
loading: false
}
case AUTH_FAIL:
return {
...state,
error: action.error
}
default:
return state
}
}
But if i hit submit one more time, it works. But i really want my user to be immediately redirected. How can I fix this ??
B
Thank you so much ;(
I would suggest removing the history.push code and let react-router handle the redirection:
import { Route, Redirect } from 'react-router'
...
<Switch>
<Route exact
path="/:category"
render={routerProps => <HomePageStream {...routerProps}/>}
/>
<Route exact
path="/article/:slug"
render={routerProps => <ArticleDetails {...routerProps}/>}
/>
<Route exact
path="/authentication/login"
render={ routerProps => (this.props.isAuthenticated ? (<Redirect to="/"/>) :(<Login {...routerProps}{...this.props}/>))}
/>
</Switch>

Categories

Resources