我正在尝试学习和开发React Redux应用程序。在应用程序中,我有一些私人路线。如果用户转到专用路由,则应使用LogIn组件对其进行身份验证,然后将其重定向到初始路由。
问题在于,用户提交表单并通过身份验证后,Reducer不会调用LogIn组件的render方法。
我被困住了,无法找出原因。
// ../ ClientApp / src / App.js
import React from 'react';
import { Route } from 'react-router';
import { Redirect } from 'react-router-dom';
import Layout from './components/Layout';
import Home from './components/Home';
import Block from './components/Block';
import LogIn from './components/LogIn';
export const auth = {
isAuthenticated: false
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
auth.isAuthenticated
? <Component {...props} />
: <Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)} />
)
export default () => (
<Layout>
<Route exact path='/' component={Home} />
<PrivateRoute path='/block1' component={Block} />
<PrivateRoute path='/block2' component={Block} />
<PrivateRoute path='/block3' component={Block} />
<PrivateRoute path='/block4' component={Block} />
<PrivateRoute path='/block5' component={Block} />
<PrivateRoute path='/block7' component={Block} />
<Route path='/login' component={LogIn} />
</Layout>
);
// ../ ClientApp / src / components / LogIn.js
import React, { Component } from 'react';
import { connect } from "react-redux";
import { bindActionCreators } from 'redux';
import './LogIn.css';
import { actionCreators } from '../store/LogIn';
import { Redirect } from 'react-router-dom';
import { auth } from '../App';
class LogIn extends Component {
state = {
credentials: {
username: '',
password: ''
},
error: ''
}
dismissError = () => {
this.setState({ error: '' });
}
handleChange = e => {
const credentials = this.state.credentials;
credentials[e.target.name] = e.target.value;
this.setState({ credentials: credentials });
}
handleSubmit = (e) => {
e.preventDefault();
if (!this.state.credentials.username) {
return this.setState({ error: 'This field is required' });
}
if (!this.state.credentials.password) {
return this.setState({ error: 'This field is required' });
}
this.props.requestLogIn(this.state.credentials);
}
render() {
auth.isAuthenticated = this.props.isAuthenticated;
const { credentials } = this.state;
if (this.props.redirectToReferrer) {
const { from } = this.props.location.state || {
from: { pathname: '/' }
}
return (
<Redirect to={from} />
)
}
return (
<div className="container">
<div className="row">
<div className="col-md-6 col-md-offset-3">
<div className="panel panel-login">
<div className="panel-heading">
<div className="row">
<div className="col-xs-6">
<a href="/" className="active" id="login-form-link">Log in</a>
</div>
</div>
<hr />
</div>
<div className="panel-body">
<div className="row">
<div className="col-lg-12">
<form id="login-form" onSubmit={this.handleSubmit} style={{ display: 'block' }}>
{
this.state.error &&
<h3 data-test="error" onClick={this.dismissError}>
<button onClick={this.dismissError}>X</button>
{this.state.error}
</h3>
}
<div className="form-group">
<input
type="text"
name="username"
tabIndex="1"
className="form-control"
placeholder="E-mail"
value={credentials.username}
onChange={this.handleChange} />
</div>
<div className="form-group">
<input
type="password"
name="password"
tabIndex="2"
className="form-control"
placeholder="Password"
value={credentials.password}
onChange={this.handleChange} />
</div>
<div className="form-group">
<div className="row">
<div className="col-sm-6 col-sm-offset-3">
<input
type="submit"
tabIndex="3"
className="form-control btn btn-login"
value="Log in" />
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
isAuthenticated: state.isAuthenticated,
redirectToReferrer: state.redirectToReferrer
}
}
export default connect(
mapStateToProps,
dispatch => bindActionCreators(actionCreators, dispatch)
)(LogIn);
// ../ ClientApp / src / store / LogIn.js
const authenticated = 'AUTHENTICATED_USER';
const unauthenticated = 'UNAUTHENTICATED_USER';
const authenticationError = 'AUTHENTICATION_ERROR';
const initialState = {
isAuthenticated: false,
redirectToReferrer: false,
error: '',
token: ''
}
export const actionCreators = {
requestLogIn: ({ username, password }) => async (dispatch) => {
try {
const response = await fetch('api/Authentication/Authenticate',
{
method: 'POST',
body: JSON.stringify({
username: username,
password: password
}),
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin'
});
const token = await response.text();
dispatch({
type: authenticated,
token
});
} catch (e) {
console.log(e);
dispatch({
type: authenticationError,
error: 'Invalid email or password'
});
}
}
}
export const reducer = (state, action) => {
state = state || initialState;
switch (action.type) {
case authenticated:
return {
...state,
isAuthenticated: true,
redirectToReferrer: true,
token: action.token
};
case unauthenticated:
return { ...state, isAuthenticated: false };
case authenticationError:
return { ...state, isAuthenticated: false, error: action.error };
}
return state;
}
更新: 感谢remix23的回答。他说的对,我有几个减速器,我必须在mapStateToProps函数中指向 logIn 减速器,如下所示:
const mapStateToProps = state => {
return {
isAuthenticated: state.logIn.isAuthenticated,
redirectToReferrer: state.logIn.redirectToReferrer,
error: state.logIn.error,
token: state.logIn.token
}
}
仅供参考(也许对某人有用),这是我的减速器配置:
//../ ClientApp / src / store / configureStore.js:
import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import thunk from 'redux-thunk';
import { routerReducer, routerMiddleware } from 'react-router-redux';
import * as Items from '../reducers/items';
import * as LogIn from './LogIn';
export default function configureStore(history, initialState) {
const reducers = {
items: Items.reducer,
logIn: LogIn.reducer
};
const middleware = [
thunk,
routerMiddleware(history)
];
// In development, use the browser's Redux dev tools extension if installed
const enhancers = [];
const isDevelopment = process.env.NODE_ENV === 'development';
if (isDevelopment && typeof window !== 'undefined' && window.devToolsExtension) {
enhancers.push(window.devToolsExtension());
}
const rootReducer = combineReducers({
...reducers,
routing: routerReducer
});
return createStore(
rootReducer,
initialState,
compose(applyMiddleware(...middleware), ...enhancers)
);
}
答案 0 :(得分:1)
路径// ../ClientApp/src/store/LogIn.js
建议您在store
文件夹下定义几个减速器。
这通常意味着您还具有一个“应用程序” reduce(所有的reduce以及它们各自的键的组合)。
如果大小写和登录还原程序的键是login
,则在您提供的mapStateToProps中,您可能必须以这种方式访问isAuthenticated值(否则state.isAuthenticated将保持未定义状态):>
const mapStateToProps = state => {
return {
isAuthenticated: state.login.isAuthenticated,
...
}
}
还有其他建议,从身份验证存储的初始值访问身份验证是不好的,即使它看起来可行,因为您在登录组件中进行了设置。
您应该像使用登录一样连接App,并通过道具访问isAuthenticated(并且永远不要设置商店初始状态的值)。