In my component A, I want to get the isAuthenticated prop from Redux state. It’s always null, which is the initial value, whereas in all other components it’s always the latest value.
When user authenticates I send their credentials to the server. If it’s a success I store the token in Redux state. But my component A doesn’t see that token.
I tried to connect the Redux store directly to the component A and extract that isAuthenticated, but again it’s always null when I console.log(this.props.isAuthenticated)
The full code is in the GitHub repo
Component A:
import React from "react";
import { connect } from "react-redux";
import { auth } from "../../store/actions/index";
import { Redirect } from "react-router-dom";
import Input from "../../components/UI/input/input";
import Button from "../../components/UI/button/button";
class Auth extends React.Component {
  state = {
    controls: {
      email: {
        elType: "input",
        elConfig: {
          type: "email",
          name: "email",
          placeholder: "Email",
          label: "Email"
        },
        value: "",
        validation: {
          required: true,
          isEmail: true
        },
        valid: false,
        touched: false
      },
      password: {
        elType: "input",
        elConfig: {
          type: "password",
          name: "password",
          label: "Password",
          placeholder: "Password"
        },
        value: "",
        validation: {
          required: true,
          minLength: 9
        },
        valid: false,
        touched: false,
        redirect: null
      }
    }
  };
  checkValidity = (value, rules) => {
    let valid = true;
    if (!rules) return true;
    if (rules.required) valid = value.trim() !== "" && valid;
    if (rules.minLength) valid = value.length >= rules.minLength && valid;
    if (rules.maxLength) valid = value.length <= rules.maxLength && valid;
    if (rules.isEmail) {
      const pattern = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/;
      valid = pattern.test(value) && valid;
    }
    return valid;
  };
  inputChangedHandler = (e, elId) => {
    const updatedControls = {
      ...this.state.controls,
      [elId]: {
        ...this.state.controls[elId],
        value: e.target.value,
        valid: this.checkValidity(
          e.target.value,
          this.state.controls[elId].validation
        ),
        touched: true
      }
    };
    this.setState({
      controls: updatedControls
    });
  };
  signUpRedirect = () => {
    this.setState({
      redirect: "/sign-up"
    });
  };
  signInRedirect = () => {
    this.setState({
      redirect: "/sign-in"
    });
  };
  submitHandler = e => {
    e.preventDefault();
    this.props.onAuth(
      this.state.controls.email.value,
      this.state.controls.password.value,
      this.props.signUp
    );
  };
  render() {
    console.log(this.props.isAuthenticated);
    console.log(this.props);
    const formElementsArray = [];
    const { controls } = this.state;
    for (let key in controls) {
      formElementsArray.push({
        id: key,
        config: controls[key]
      });
    }
    const form = formElementsArray.map(el => (
      <Input
        key={el.id}
        elType={el.config.elType}
        elConfig={el.config.elConfig}
        value={el.config.value}
        shouldValidate={el.config.validation}
        touched={el.config.touched}
        valid={el.config.valid}
        name={el.config.elConfig.name}
        label={el.config.elConfig.label}
        placeholder={el.config.elConfig.placeholder}
        changed={e => this.inputChangedHandler(e, el.id)}
      />
    ));
    return (
      <section className="section-auth">
        {this.state.redirect !== null && this.state.redirect !== undefined && (
          <Redirect to={this.state.redirect} />
        )}
        <form onSubmit={this.submitHandler} className="section-auth__form">
          <h2 className="section-auth__title">
            {this.props.signUp ? "Sign up" : "Sign in"}
          </h2>
          {form}
          <Button
            type="submit"
            className="button--secondary"
            style={{
              marginTop: "2rem"
            }}
          >
            SUBMIT
          </Button>
          {this.props.signUp ? (
            <div className="section-auth__btn-box">
              <p className="section-auth__text">Already have an account?</p>
              <Button
                clicked={this.signInRedirect}
                type="button"
                className="button--primary section-auth__btn"
              >
                Sign in
              </Button>
            </div>
          ) : (
            <div className="section-auth__btn-box">
              <p className="section-auth__text">Don't have an account?</p>
              <Button
                clicked={this.signUpRedirect}
                type="button"
                className="button--primary section-auth__btn"
              >
                Sign up
              </Button>
            </div>
          )}
        </form>
      </section>
    );
  }
}
const mapStateToProps = state => ({
  isAuthenticated: state.auth.token
});
const mapDispatchToProps = dispatch => ({
  onAuth: (email, password, signUp) => dispatch(auth(email, password, signUp))
});
export default connect(mapStateToProps, mapDispatchToProps)(Auth);
Action creators:
import * as firebase from "firebase";
import {
  AUTH_START,
  AUTH_SUCCESS,
  AUTH_FAIL,
  AUTH_SIGNOUT
} from "./actionTypes";
const tokenExpTimeInMs = 3600000;
const saveToLocalStorage = (token, expDate, userId) => {
  localStorage.setItem("token", token);
  localStorage.setItem("expirationDate", expDate);
  localStorage.setItem("userId", userId);
};
export const authStart = () => ({ type: AUTH_START });
export const authSuccess = (token, userId) => ({
  type: AUTH_SUCCESS,
  idToken: token,
  userId: userId
});
export const authFail = error => ({ type: AUTH_FAIL, error: error });
export const authSignout = () => {
  localStorage.removeItem("token");
  localStorage.removeItem("expirationDate");
  localStorage.removeItem("userId");
  return {
    type: AUTH_SIGNOUT
  };
};
export const checkAuthTimeout = expTime => dispatch => {
  setTimeout(() => {
    dispatch(authSignout());
  }, expTime);
};
export const auth = (email, password, signUp) => dispatch => {
  dispatch(authStart());
  if (signUp) {
    firebase
      .auth()
      .createUserWithEmailAndPassword(email, password)
      .then(response => {
        response.user.getIdToken().then(token => {
          const expirationDate = new Date(
            new Date().getTime() + tokenExpTimeInMs
          );
          saveToLocalStorage(token, expirationDate, response.user.uid);
          dispatch(checkAuthTimeout(tokenExpTimeInMs));
          dispatch(authSuccess(token, response.user.uid));
        });
      })
      .catch(error => {
        console.log(error);
        dispatch(authFail(error));
      });
  } else {
    firebase
      .auth()
      .signInWithEmailAndPassword(email, password)
      .then(response => {
        response.user.getIdToken().then(token => {
          const expirationDate = new Date(
            new Date().getTime() + tokenExpTimeInMs
          );
          saveToLocalStorage(token, expirationDate, response.user.uid);
          dispatch(checkAuthTimeout(tokenExpTimeInMs));
          dispatch(authSuccess(token, response.user.uid));
        });
      })
      .catch(error => {
        console.log(error);
        dispatch(authFail(error));
      });
  }
};
export const authCheckState = () => dispatch => {
  const token = localStorage.getItem("token");
  if (!token) {
    dispatch(authSignout());
  } else {
    const expDate = new Date(localStorage.getItem("expirationDate"));
    if (expDate <= new Date()) {
      dispatch(authSignout());
    } else {
      const userId = localStorage.getItem("userId");
      dispatch(authSuccess(token, userId));
      dispatch(checkAuthTimeout(expDate.getTime() - new Date().getTime()));
    }
  }
};
Reducer:
import {
  AUTH_START,
  AUTH_SUCCESS,
  AUTH_FAIL,
  AUTH_SIGNOUT
} from "../actions/actionTypes";
const updateObject = (oldObject, updatedProperties) => {
  return {
    ...oldObject,
    ...updatedProperties
  };
};
const initialState = {
  token: null,
  userId: null,
  error: null
};
const authStart = (state, action) => updateObject(state, { error: null });
const authSuccess = (state, action) => {
  return updateObject(state, {
    token: action.idToken,
    userId: action.userId,
    error: null
  });
};
const authFail = (state, action) =>
  updateObject(state, {
    error: action.error
  });
const authSignout = (state, action) =>
  updateObject(state, { token: null, userId: null });
export default (state = initialState, action) => {
  switch (action.type) {
    case AUTH_START:
      return authStart(state, action);
    case AUTH_SUCCESS:
      return authSuccess(state, action);
    case AUTH_FAIL:
      return authFail(state, action);
    case AUTH_SIGNOUT:
      return authSignout(state, action);
    default:
      return state;
  }
};
Redux store:
import thunk from "redux-thunk";
import { createStore, combineReducers, applyMiddleware, compose } from "redux";
import authReducer from "./reducers/auth";
import userCdtReducer from "./reducers/userCountdownTimers";
import eventFormReducer from "./reducers/eventForm";
const rootReducer = combineReducers({
  auth: authReducer,
  userCdt: userCdtReducer,
  eventData: eventFormReducer
});
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
export default createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunk))
);
 
     
    