В моем компоненте A я хочу получить реквизит isAuthenticated
из состояния Redux. Это всегда null
, что является начальным значением, тогда как во всех других компонентах это всегда самое последнее значение.
Когда пользователь аутентифицируется, я отправляю его учетные данные на сервер. В случае успеха я сохраняю token
в состоянии Redux. Но мой компонент A не видит, что token
.
Я пытался подключить хранилище Redux напрямую к компоненту A и извлечь это isAuthenticated
, но опять же, когда я console.log(this.props.isAuthenticated)
* 1011, он всегда равен нулю. *
Полный код находится в репозитории GitHub
Компонент 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);
Создатели действий:
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()));
}
}
};
Редуктор:
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:
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))
);