2016-03-07 121 views
34

我想利用react-router的onEnter處理程序來提示用戶在進入受限制的路由時進行身份驗證。從通過React路由器設置的路由訪問Redux存儲

到目前爲止,我routes.js文件看起來是這樣的:

import React from 'react'; 
import { Route, IndexRoute } from 'react-router'; 

export default (
    <Route path="/"   component={App}> 
     <IndexRoute    component={Landing} /> 
     <Route path="learn"  component={Learn} /> 
     <Route path="about"  component={About} /> 
     <Route path="downloads" component={Downloads} onEnter={requireAuth} /> 
    </Route> 
) 

理想情況下,我想我的requireAuth功能是一個終極版的行動,可以訪問存儲和當前的狀態,即是這樣的: store.dispatch(requireAuth())

很抱歉,我無法使用此文件中的存儲區。我不認爲我可以在這種情況下真正使用connect來訪問我想要的相關操作。我也不能從創建商店的文件中獲得import store,因爲在應用第一次加載時這是未定義的。

回答

53

完成此操作的最簡單方法是將您的商店傳遞給返回您的路線的函數(而不是直接返回您的路線)。通過這種方式,您可以通過onEnter和其他反應路由器方法訪問商店。

因此,對於你的路線:

import React from 'react'; 
import { Route, IndexRoute } from 'react-router'; 

export const getRoutes = (store) => (
    const authRequired = (nextState, replaceState) => { 
    // Now you can access the store object here. 
    const state = store.getState(); 

    if (!state.user.isAuthenticated) { 
     // Not authenticated, redirect to login. 
     replaceState({ nextPathname: nextState.location.pathname }, '/login'); 
    } 
    }; 

    return (
    <Route path="/"   component={App}> 
     <IndexRoute    component={Landing} /> 
     <Route path="learn"  component={Learn} /> 
     <Route path="about"  component={About} /> 
     <Route path="downloads" component={Downloads} onEnter={authRequired} /> 
    </Route> 
); 
) 

然後更新您的主要組件來調用getRoutes功能,通過在賣場:

<Provider store={ store }> 
    <Router history={ history }> 
    { getRoutes(store) } 
    </Router> 
</Provider> 

至於派遣由requireAuth一個動作,你可以寫你的功能是這樣的:

const authRequired = (nextState, replaceState, callback) => { 
    store.dispatch(requireAuth()) // Assume this action returns a promise 
    .then(() => { 
     const state = store.getState(); 

     if (!state.user.isAuthenticated) { 
     // Not authenticated, redirect to login. 
     replaceState({ nextPathname: nextState.location.pathname }, '/login'); 
     } 

     // All ok 
     callback(); 
    }); 
}; 

Hope這有助於。

+0

這就是一個很好的例子。謝謝大家:) – shivam

+1

這將如何與服務器渲染工作? – Enkay

+0

TY有些多,這個方法真的很容易tom實現,但是我需要問一下,有沒有這個退縮? – StormRage

12

如果你想,你可以寫route.js這樣的:

var requireAuth = (store, nextState, replace) => { 
    console.log("store: ", store); 
    //now you have access to the store in the onEnter hook! 
} 

export default (store) => { 
    return (
     <Route path="/"   component={App}> 
     <IndexRoute    component={Landing} /> 
     <Route path="learn"  component={Learn} /> 
     <Route path="about"  component={About} /> 
     <Route path="downloads" component={Downloads} onEnter={requireAuth.bind(this, store)} /> 
     </Route> 
    ); 
); 

我設置,你可以用這個codepen打一個比方。

不確定是否觸發操作以處理auth是個好主意。我個人更喜歡在以不同的方式處理身份驗證

而不是使用onEnter鉤子,我使用包裝功能。我希望我的博客的管理部分受到保護,因此我將AdminContainer組件包裝在具有函數requireAuthentication的路由中,如下所示。

export default (store, history) => { 
     return (
      <Router history={history}> 
       <Route path="/" component={App}> 
        { /* Home (main) route */ } 
        <IndexRoute component={HomeContainer}/> 
        <Route path="post/:slug" component={PostPage}/> 
        { /* <Route path="*" component={NotFound} status={404} /> */ } 
       </Route> 

       <Route path="/admin" component={requireAuthentication(AdminContainer)}> 
        <IndexRoute component={PostList}/> 
        <Route path=":slug/edit" component={PostEditor}/> 
        <Route path="add" component={PostEditor}/> 
       </Route> 
       <Route path="/login" component={Login}/> 
      </Router> 
     ); 
    }; 

requireAuthentication是一個函數,

  • 如果用戶進行身份驗證,使被包裝的成分,
  • 否則重定向到Login

您可以在下面看到它:

export default function requireAuthentication(Component) { 
    class AuthenticatedComponent extends React.Component { 

     componentWillMount() { 
      this.checkAuth(); 
     } 

     componentWillReceiveProps (nextProps) { 
      this.checkAuth(); 
     } 

     checkAuth() { 
      if (!this.props.isAuthenticated) { 
       let redirectAfterLogin = this.props.location.pathname; 
       this.context.router.replace({pathname: '/login', state: {redirectAfterLogin: redirectAfterLogin}}); 
      } 
     } 

     render() { 
      return (
       <div> 
        {this.props.isAuthenticated === true 
         ? <Component {...this.props}/> 
         : null 
        } 
       </div> 
      ) 

     } 
    } 

    const mapStateToProps = (state) => ({ 
     isAuthenticated: state.blog.get('isAuthenticated') 
    }); 

    AuthenticatedComponent.contextTypes = { 
     router: React.PropTypes.object.isRequired 
    }; 

    return connect(mapStateToProps)(AuthenticatedComponent); 
} 

此外,requireAuthentication將保護/admin下的所有路線。你可以在任何你喜歡的地方重複使用它。

+0

看起來像AuthenticatedComponent是用於非可視路由驗證檢查目的的可視React組件的用法。你不認爲所有這些componentWillMount都不是關於路由驗證嗎? –

+0

我同意@ alex_1948511,這是一個破解。但是,再次,JS世界中很少有東西被很好的定義(或者,這只是我作爲JS編程中的n00b的觀點)。對於如何在React中更好地實現這一點,我願意接受任何建議。在過去的幾個月中,我沒有看到這一點,因爲我發現這種方法在互聯網上的某個地方,我沒有看得更遠。 :-) –

+0

我可以在路由器v4中添加你不能嵌套路由器標籤。這會引發錯誤 –

2

很多時候都發生了變化。 onEnter不再存在於react-router-4

以下是我真實的項目,供您參考

export const getRoutes = (store) => { 
 
    const PrivateRoute = ({ component: Component, ...rest }) => (
 
    <Route {...rest} render={props => (
 
     checkIfAuthed(store) ? (
 
     <Component {...props}/> 
 
    ) : (
 
     <Redirect to={{ 
 
      pathname: '/login' 
 
     }}/> 
 
    ) 
 
    )}/> 
 
) 
 

 
    return (
 
    <Router> 
 
     <div> 
 
     <PrivateRoute exact path="/" component={Home}/> 
 
     <Route path="/login" component={Login} /> 
 
     </div> 
 
    </Router> 
 
) 
 
}