如何正确使用compose来组合在React-Redux中封装受保护路由的HOC

时间:2017-09-26 19:11:42

标签: reactjs redux react-redux

我显然没有抓住composeRedux的概念,因为我无法按预期工作。

我目前在我的一条受保护路线上有两个嵌套的HOC:一个确保用户在导航到受保护路由之前已经过验证,而另一个只是一个计时器,如果它们处于空闲状态,则会将它们从受保护路由中踢出。 / p>

我想利用compose以便我可以清理我的代码,而不是将我的受保护路由嵌套在HOC中。

这就是我目前所拥有的:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { routerMiddleware, ConnectedRouter } from 'react-router-redux';

import createHistory from 'history/createBrowserHistory';

// Render on every route
import App from './components/app';

// Route specific
import Signin from './containers/authentication/signin';
import Signout from './containers/authentication/signout';
import Home from './components/home';

// HoC to wrap protected routes in
import RequireAuth from './helpers/require_auth';
import Timer from './helpers/timer';

// Reducers 
import rootReducer from './reducers';

// SCSS for the project
import styles from '../assets/scss/main.scss';

const history = createHistory();

const initialState = {};
const enhancers = [];
const middleware = [thunk, routerMiddleware(history)];

if (process.env.NODE_ENV === 'development') {
    const devToolsExtension = window.devToolsExtension

    if (typeof devToolsExtension === 'function') {
        enhancers.push(devToolsExtension())
    }
}

const composedEnhancers = compose(applyMiddleware(...middleware), ...enhancers);
const store = createStore(rootReducer, initialState, composedEnhancers);

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <div>
                <App />
                <Switch>
                    <Route exact path='/home' component={LeftSite(RequireAuth(Home))} />
                    <Route exact path='/auth/signout' component={Signout} />
                    <Route exact path='/auth/signin' component={Signin} />
                    <Route exact path='/' component={Signin} />
                </Switch>
            </div>
        </ConnectedRouter>
    </Provider>
    , document.querySelector('.container'));

我试过的是这样修改:

const composedEnhancers = compose(
                            applyMiddleware(...middleware),
                            ...enhancers
                        );

const protectedRoutes = compose(
                            applyMiddleware(RequireAuth, LeftSite)                            
                        );

const store = createStore(rootReducer, initialState, composedEnhancers, protectedRoutes);

ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
            <div>
                <App />
                <Switch>
                    <Route exact path='/home' component={protectedRoutes(Home)} />
                    <Route exact path='/auth/signout' component={Signout} />
                    <Route exact path='/auth/signin' component={Signin} />
                    <Route exact path='/' component={Signin} />
                </Switch>
            </div>
        </ConnectedRouter>
    </Provider>
    , document.querySelector('.container'));

这只会产生Cannot call a class as a function。这是其中一个HOC,因为它们都具有相似的结构:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { authRequired } from '../actions/authentication';

export default function(ComposedComponent) {
    class AuthenticationRequired extends Component {

        // Check if the user has a token when protected route component is about to mount
        // Route them to / if they do not
        componentWillMount() {
            if (!sessionStorage.getItem('token')) {
                this.props.authRequired();
            }
        }

        // If the component will be updated then also check if user is authorized
        componentWillUpdate() {
            if (!sessionStorage.getItem('token')) {
                this.props.authRequired();
            }
        }

        // Render the component if passing these checks
        render() {
            return <ComposedComponent {...this.props} />
        }
    }

    // Conenct to the authRequired action
    return connect( null, { authRequired })(AuthenticationRequired);
}

我尝试了多种不同错误的变体。我不打算发布所有内容。我在这里看起来似乎可能更接近正确答案......也许?

我确实阅读了在我的案例中没有找到帮助的文档:

https://paulkogel.gitbooks.io/redux-docs/content/docs/api/compose.html

所以我的问题是:

我应该如何构建compose

我应该如何在受保护的路线上调用它?

1 个答案:

答案 0 :(得分:1)

就像文档说的那样,O(n)实际上在概念上做了一些非常简单的事情:

  

All compose does is let you write deeply nested function transformations without the rightward drift of the code. Don’t give it too much credit!

因此,假设你有三个非常简单的函数:

compose

如果您想连续使用全部三个,而不使用const capitalize = str => str.toUpperCase(); const takeFirstChar = str => str[0]; const prependHello = str => 'hello' + str; ,它将成为嵌套调用:

compose

看看第一个被调用的函数(const originalValue = 'test'; console.log( capitalize(takeFirstChar(prependHello(originalValue)))) // prints 'H' // (the first letter from the appended 'hello', capitalized) )是如何一直向右的?这就是“代码向右漂移”的含义。

但是对于prependHello,我们可以这样写:

compose

目前还不清楚你在工作之前如何使用你的HOC,但如果你之前没有涉及// NOTE the order of the arguments! // The bottom one is applied first, then middle one, then the top one const prependHelloAndTakeFirstCharToUpper = compose( capitalize, takeFirstChar, prependHello ); console.log( prependHelloAndTakeFirstCharToUpper(originalValue) ) // still prints 'H' 这样的事情,那么现在可能不应该在那里。

我的猜测是,在不了解您的代码的情况下,它应该能够做到这样的事情:

applyMiddleWare

我认为您可以将const protectedRoutes = compose(RequireAuth, LeftSite); // further down <Route exact path='/home' component={protectedRoutes(Home)} /> 从您的论据中删除protectedRoutes