In a project where the front and back ends are separated, we usually meet the requirement of implementing front-end route permission and global loading effect. In a Vue project, we can implement global loading effect when entering a route through the two hook functions of route guard beforEach and afterEach. Vue-router also provides flexible route configuration items that allow us to assign more information to routes, including permissions and so on. The React-Router, on the other hand, does not provide such a component directly. While vue-Router itself provides flexible configuration, React advanced components also give us great opportunities.

Encapsulating routing component

const App: React.FC = () => (
  <Provider store={store}>
    <div className="App">
      <Switch>
        <AuthRoute config={RouteConfig} />
      </Switch>
    </div>
  </Provider>
);

export default withRouter(App);
Copy the code

On the outside we don’t use the Route component provided by the React-Router. Instead we use our own Route component, which takes a config parameter and passes in the Route configuration. This way we can also write the Route configuration file as in VUE.

Routing profile

Defines the type of a single route configuration

export interface RouteItem { path: string; component? : FC; auth? : boolean; }Copy the code

Finally, the exported route configuration information is an array consisting of RouteItems. Path indicates the routing path, Component indicates the corresponding component, and auth indicates whether authentication is required. If there are multiple roles, set auth to the role name and add the judgment method.

Redux design for global loading

Since loading is to be done globally, redux is the best choice. I’ll just post the code here, so I won’t go into details about Redux. Because combineReducers are used, we put the loading state into the reducer app.

actionTypes.ts

const SET_LOADING = 'SET_LOADING';

export default {
  /** * Set the loading state */
  SET_LOADING,
};
Copy the code

app.action.ts

import actionTypes from './actionTypes';

export const setLoading = (newStatus: boolean) = > ({
  type: actionTypes.SET_LOADING,
  data: newStatus,
});
Copy the code

app.reducer.ts

import actionTypes from './actionTypes';

export interface AppState {
  loading: boolean;
}

const defaultState: AppState = {
  loading: false};export default (state = defaultState, action: any) = > {switch (action.type) {
    case actionTypes.SET_LOADING:
      return { ...state, loading: action.data };
    default:
      returnstate; }};Copy the code

Implement AuthRoute components

Since the AuthRoute component resides inside the Switch component, the React Router also automatically injects the AuthRoute location attribute. When the route in the address bar changes, This triggers a change in the PathName property on the Location property object, and we can use this change to match the previously written route configuration to get the corresponding component and re-render it.

Implementing global loading

We only need to wrap a layer of Spin component outside the Route component. The loading state of the Spin component is the loading state in Redux. If the loading time needs to be determined according to the network request, we only need to set the loading value in the corresponding component. I’m just going to use the timer here just to make it easier to see.

code

const AuthRoute: React.FC<any> = props => { const dispatch = useDispatch(); const loading: boolean = useSelector((state: Store) => state.app.loading); const { pathname } = props.location; const isLogin = localStorage.getItem('user_token'); let timer = 0; useEffect(() => { window.scrollTo(0, 0); dispatch(setLoading(true)); clearTimeout(timer); timer = window.setTimeout(() => { dispatch(setLoading(false)); }, 1000); }, [pathname]); const targetRouterConfig: RouteItem = props.config.find( (v: RouteItem) => v.path === pathname ); if (targetRouterConfig && ! targetRouterConfig.auth && ! isLogin) { const { component } = targetRouterConfig; return <Route exact path={pathname} component={component} />; } if (isLogin) {// If (pathName === '/login') {return <Redirect to="/" />; } // If the route is valid, If (targetRouterConfig) {return (<Spin tip="Loading" size="large" spinning={Loading} // indicator={<Icon type="loading" style={{ fontSize: 24 }} spin />} style={{ maxHeight: 'none' }} > <Route path={pathname} component={targetRouterConfig.component} /> </Spin> ); Return <Redirect to="/404" />; } // In non-login state, when the route is valid and permission verification is required, the login page is displayed. If (targetRouterConfig && targetrouterconfig. auth) {return <Redirect to="/login" />; } return <Redirect to="/404" />; }; export default AuthRoute;Copy the code

Refer to the article

  • React Router 4.0 implements route guard