路由/重定向不会在React Router 4& Redux授权

时间:2017-04-19 08:01:16

标签: javascript reactjs authentication redux react-router

我正在尝试使用React Router 4 + Redux实现一个简单的授权流程。

我想要的行为

  • 如果authorization(商店中的布尔值)为false,则任何路由都应重定向到登录页面。
  • 如果authorizationtrue,则任何路线都应呈现该路线。
  • 如果在用户登录时请求登录路由,则应将其重定向到应用程序的根路由。

我的实施

  • 我创建了一个放在容器中的组件<CheckAuth>(即react-redux connect),用于访问商店中的authorization布尔值。
  • <CheckAuth>接受两个组件<Protected><LoginPage>,并根据布尔值决定一个或另一个。
  • <CheckAuth>内,根据<Route>,呈现了<Redirect>所请求的组件或authorization
  • <LoginPage>中的表单会调度LOGIN操作,将authorization布尔值设置为true。
  • <Protected>中的表单将调度LOGOUT操作,将authorization布尔值设置为false。 (此表单应与<Protected>分开,但我将它们放在一起以保持示例简单。)

问题

我遇到的问题是,当商店发生变化时,组件似乎没有更新。对页面进行硬刷新会更新视图,但它也会消除商店,并且只会出错。

我可能在某处做了一些错误的假设。如果有人可以提供帮助,我将不胜感激!

代码

我创建了一个简化版的代码,如下所示,可以粘贴到create-react-app App.js文件中。下面是一个package.json文件,可以启动并运行它。 (在我正在使用的版本中,auth会持久化为cookie,因此刷新不会清除身份验证。)

除了可能提供的任何解决方案之外,我还喜欢调试react应用程序的任何提示,尤其是路由器。

App.js:

// import Libs
import React, { Component } from "react"
import { BrowserRouter as Router, Route, Redirect } from "react-router-dom"
import { createStore, applyMiddleware } from "redux"
import { Provider, connect } from "react-redux"
import { createLogger } from "redux-logger"

const loggerMiddleware = createLogger()

// create Store/Reducer
const store = createStore((state = { authenticated: false }, action) => {
    switch (action.type) {
        case "LOGIN":
            return {
                authenticated: true
            }
        case "LOGOUT":
            return {
                authenticated: false
            }
        default:
            return state
    }
}, applyMiddleware(loggerMiddleware))

// <CheckAuth> component
const CheckAuth = ({ authenticated, location, LoginPage, Protected }) => {
    const { pathname } = location
    if (authenticated) {
        if (pathname === "/login") {
            return <Redirect to="/" />
        }
        return <Route path={pathname} component={Protected} />
    }
    if (pathname === "/login") {
        return <Route path="/login" component={LoginPage} />
    }
    return <Redirect to="login" />
}

const mapStateToProps = state => ({
    authenticated: state.authenticated
})

const CheckAuthContainer = connect(mapStateToProps)(CheckAuth)

// Log in and out Actions
const authenticate = () => ({
    type: "LOGIN"
})

const removeAuth = () => ({
    type: "LOGOUT"
})

// <Login> component
const Login = ({ dispatch }) => {
    const login = e => {
        e.preventDefault()
        dispatch(authenticate())
    }
    return (
        <form onSubmit={login}>
            <button type="Submit">Login</button>
        </form>
    )
}

// <LoginPage> component
const LoginContainer = connect()(Login)

const ProtectedPage = ({ dispatch }) => {
    const logout = e => {
        e.preventDefault()
        dispatch(removeAuth())
    }
    return (
        <div>
            <h1>Protected</h1>
            <form onSubmit={logout}>
                <button type="Submit">Logout</button>
            </form>
        </div>
    )
}

const ProtectedPageContainer = connect()(ProtectedPage)

class App extends Component {
    render() {
        return (
            <Provider store={store}>
                <Router>
                    <CheckAuthContainer
                        LoginPage={LoginContainer}
                        Protected={ProtectedPageContainer}
                        location={location}
                    />
                </Router>
            </Provider>
        )
    }
}

export default App

的package.json:

{
  "name": "test-redux-router-redirect",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4",
    "react-redux": "^5.0.4",
    "react-router-dom": "^4.1.1",
    "redux": "^3.6.0",
    "redux-logger": "^3.0.1",
    "redux-thunk": "^2.2.0"
  },
  "devDependencies": {
    "react-scripts": "0.9.5"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
}

1 个答案:

答案 0 :(得分:4)

我明白了。

The React Router documentation's section on Redux integration实际上直接解决了这个问题,但不知怎的,我误解了如何应用它。

<CheckAuthContainer>组件传递到withRouter解决了我的问题。所以第45行看起来像这样:

const CheckAuthContainer = withRouter(connect(mapStateToProps)(CheckAuth))

我还没有在我正在开发的真实应用程序中试过这个。希望它也能在那里工作!