【问题标题】:In a CRA app, how to wait for some action(redux) to get complete first and then only proceed with the App.js render() function?在 CRA 应用程序中,如何先等待某些操作(redux)完成,然后才继续使用 App.js 的 render() 函数?
【发布时间】:2019-08-04 02:03:27
【问题描述】:

我正在尝试找出一种方法将用户的身份验证状态存储在 redux 存储中。假设isAuthenticated 存储用户是否登录的状态。现在,我有一个由服务器发送的 cookie(httpOnly),它会记住用户,这样他们就不需要在每次访问应用程序时都输入凭据。
流程:用户某天登录到应用程序但没有注销并关闭浏览器。现在,他回来访问我的应用程序。由于 cookie 存在于浏览器中,这将由应用程序自动发送(无需用户交互),如果 cookie 有效,isAuthenticated: true。非常简单的需求。
跟踪身份验证状态应该是应用程序完成的第一件事,所以我把这个逻辑放在最前面,在 App.js 呈现之前。

class App extends Component {

  store = configureStore();

  render() {
    return (
      <Provider store={this.store}>
        <ConnectedRouter history={history}>
          <>
            <GlobalStyle />
              <SiteHeader />
              <ErrorWrapper />
              <Switch>
                <PrivateHomeRoute exact path="/" component={Home} />
                <Route exact path="/login" component={LoginPage} />
                <PrivateHomeRoute path="/home" component={Home} />
             ........code
}

这是configureStore()

export const history = createBrowserHistory();

const configureStore = () => {
  const sagaMiddleware = createSagaMiddleware();

  const store = createStore(
    rootReducer(history),
    composeEnhancers(applyMiddleware(sagaMiddleware, routerMiddleware(history)))
  );
  sagaMiddleware.run(rootSaga);

  store.dispatch({ type: AUTH.AUTO_LOGIN });

  return store;
};

store.dispatch({ type: AUTH.AUTO_LOGIN }); 是我尝试应用程序执行自动登录作为应用程序中的第一个操作的代码。此操作由redux-saga 处理

function* handleAutoLogin() {
  try {
    const response = yield call(autoLoginApi);
    if (response && response.status === 200) {
      yield put(setAuthenticationStatus(true));
    }
  } catch (error) {
    yield put(setAuthenticationStatus(false));
  }
}

function* watchAuthLogin() {
  yield takeLatest(AUTH.AUTO_LOGIN, handleAutoLogin);
}

autoLoginApi 是对服务器的axios 调用,它将携带cookie。 setAuthenticationStatus(true) 是动作创建者,它将 isAuthenticated 设置为 true false

所以,是的,这没有按预期工作。因为,应用程序应该首先设置isAuthenticated,然后继续使用 App.js 的 render()。但是,由于设置isAuthenticated 需要一些时间(api 调用),因此应用程序首先使用isAuthenticated: false 进行渲染,然后在完成AUTH.AUTO_LOGIN 之后,应用程序会为经过身份验证的用户重新渲染。

那有什么问题呢?对于普通组件可能不是问题,例如这个SiteHeader组件

class SiteHeader extends React.Component {
  render() {
    const { isLoggedIn } = this.props;

    if (isLoggedIn === null) {
      return "";
    } else {
      if (isLoggedIn) {
        return (
          <LoggedInSiteHeader />
        );
      } else {
        return (
          <LoggedOutSiteHeader />
        );
      }
    }
  }
}

const mapStateToProps = ({ auth, user }) => ({
  isLoggedIn: auth.isLoggedIn,
});

export default connect(
  mapStateToProps,
  null
)(SiteHeader);

但是,此解决方案不适用于自定义路由。

const PrivateHomeRoute = ({ component: ComponentToRender, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      props.isLoggedIn ? (
        <ComponentToRender {...props} />
      ) : (
        <Redirect to="/login" />
      )
    }
  />
);

const mapStateToProps = auth => ({
  isLoggedin: auth.isLoggedIn
});

export default connect(
  mapStateToProps,
  null
)(PrivateHomeRoute);

PrivateHomeRoute 在 redux 存储更新之前得到解决,因此路由总是转到"/login"

我正在寻找一种解决方案,在该解决方案中,应用程序在身份验证操作未完成之前不会继续进行。但是,我不知道该把代码放在什么地方以及放在哪里?

我尝试了几件事:

  1. async await on configureStore() - 错误来了
  2. async awaitApp.js - 错误

PS:我正在使用的库 redux、redux-saga、react-router-dom、connected-react-router、axios

【问题讨论】:

标签: reactjs redux react-redux redux-saga connected-react-router


【解决方案1】:

我想出的一种方法:
创建一个单独的组件MyRouteWrapper,它将根据isLoggedIn 状态返回路由。为了解决这个问题,我停止渲染路由,直到自动登录更改isLoggedIn 状态。

我将isLoggedIn 的默认状态设置为null。现在,如果状态为nullMyRouteWrapper 将返回一个空字符串,一旦状态更改为true/false,它将返回路由,然后各个组件被渲染。

我更改了我的 App.js

const store = configureStore();

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <ConnectedRouter history={history}>
          <MyRouteWrapper />
        </ConnectedRouter>
      </Provider>
    );
  }
}    
export default App;

确保只有在状态变为true/false时才返回Route的组件

const MyRouteWrapper = props => {
  if (props.isLoggedIn === null) {
    return "";
  } else {
    return (
      <>
        <GlobalStyle />
        <SiteHeader />
        <ErrorWrapper />
        <Switch>
          <ProtectedHomeRoute
            exact
            path="/"
            component={Home}
            isLoggedIn={props.isLoggedIn}
          />
          <Route path="/profile/:id" component={Profile} />
          <Route path="/login" component={LoginPage} />
        </Switch>
      </>
    );
  }
};

const mapStateToProps = ({ auth }) => ({
  isLoggedIn: auth.isLoggedIn
});

export default connect(mapStateToProps)(MyRouteWrapper);

这解决了这个问题。

我仍然很想知道任何人想到的解决方案(更好)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-29
    • 2014-02-03
    • 1970-01-01
    • 2011-11-04
    相关资源
    最近更新 更多