减速器不调用渲染功能

时间:2018-09-16 10:42:30

标签: reactjs redux react-redux

我正在尝试学习和开发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)
    );
}

1 个答案:

答案 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(并且永远不要设置商店初始状态的值)。