简体   繁体   中英

make a private route to async route in react router v4

I am using react router v4 for routing in my application. The component, sagas and reducers are loaded asynchronously. How can i now implement private routing and public routing in such case?

Here is the code for routing and loading

/**
 * A wrapper component that will lazily render a component after it has been loaded.
 */
class Bundle extends Component {
  static contextTypes = {
    store: React.PropTypes.object
  };

  state = {
    // short for "module" but that's a keyword in js, so "mod"
    mod: null
  };

  componentWillMount() {
    this.load(this.props);
  }

  /* istanbul ignore next */
  componentWillReceiveProps(nextProps) {
    if (nextProps.load !== this.props.load) {
      this.load(nextProps);
    }
  }

  load(props) {
    this.setState({
      mod: null
    });
    props.load(this.context.store, mod => {
      this.setState({
        // handle both es imports and cjs
        mod: mod.default ? mod.default : mod
      });
    });
  }

  render() {
    // eslint-disable-next-line no-unused-vars
    const { load, ...otherProps } = this.props;
    return this.state.mod && <this.state.mod {...otherProps} />;
  }
}

const AsyncRoute = ({ load, ...others }) => (
  <Route {...others} render={props => <Bundle load={load} {...props} />} />
);

AsyncRoute.propTypes = {
  computedMatch: React.PropTypes.object,
  path: React.PropTypes.string,
  load: React.PropTypes.func
};

export default AsyncRoute;


// how can i make private route with different layout not a children of App
function Routes({ location }) {
  return (
    <Switch location={location}>
      <AsyncRoute exact path="/" load={loadHomePage} />
      <AsyncRoute exact path="/signup" load={loadSignupPage} />
      <AsyncRoute path="" load={loadNotFoundPage} />
    </Switch>
  );
}


export default (store, cb) => {
  const { injectReducer, injectSagas } = getAsyncInjectors(store);
  const importModules = Promise.all([
    import("./reducer"),
    import("./sagas"),
    import("./index")
  ]);

  importModules.then(([reducer, sagas, component]) => {
    injectReducer("signup", reducer.default);
    injectSagas(sagas.default);

    cb(component);
  });

  importModules.catch(errorLoading);
};

const render = messages => {
  ReactDOM.render(
    <Provider store={store}>
        <ConnectedRouter history={history}>
          <App />
        </ConnectedRouter>
    </Provider>,
    document.getElementById("app")
  );
};

Assuming you have used react-router-redux to sync your history with your redux store, you can create a saga in the main chunk that takes the latest LOCATION_CHANGE action type from react-router-redux and refresh your authentication (store it in auth reducer) by calling some API.

To distinguish between private and public components, you can modify Bundle such that it defers from asynchronously loading private chunks until the auth state is valid.

Here are some code sketch

sagas/auth.js

import { LOCATION_CHANGE } from 'react-router-redux'

function *checkAuth() {
   yield put(authenticateStart())
   // Assuming using some token authentication
   const token = yield select(state => state.auth.token)
   const result = yield call(Api.refreshAuth, token)
   if (result.isAuthenticated) {
      yield put(authenticateSuccess(result.user)
   } else {
      // Need auth
      yield put(authenticateError())
   }
}
// Put this saga on main bundle
function *authSaga() {
    yield* takeLatest(LOCATION_CHANGE, checkAuth)
}

bundle.js

class Bundle extends Component {
  static contextTypes = {
    store: React.PropTypes.object
  };

  state = {
    // short for "module" but that's a keyword in js, so "mod"
    mod: null
  };

  componentWillMount() {
    if (this.props.isProtected && !this.props.isAuthenticated)
       return
    this.load(this.props);
  }

  /* istanbul ignore next */
  componentWillReceiveProps(nextProps) {
    // Modify similar to above
  }

  load(props) {
    this.setState({
      mod: null
    });
    props.load(this.context.store, mod => {
      this.setState({
        // handle both es imports and cjs
        mod: mod.default ? mod.default : mod
      });
    });
  }

  render() {
    // eslint-disable-next-line no-unused-vars
    const { load, ...otherProps } = this.props;
    return this.state.mod && <this.state.mod {...otherProps} />;
  }
}

export default connect(state => ({ isAuthenticated: state.auth.isAuthenticated }))(Bundle)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM