В приложении CRA, как дождаться, пока какое-то действие (приставка) завершится сначала, а затем только продолжить с функцией App.js render ()? - PullRequest
0 голосов
/ 13 марта 2019

Я пытаюсь найти способ сохранить состояние аутентификации пользователя в хранилище 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 разрешается до того, как обновляется резервное хранилище, следовательно, Маршрут всегда идет к "/login".

Я ищу решение, в котором приложение не будет работать до тех пор, пока действие аутентификации не завершится.Но я понятия не имею, что и куда помещать этот код?

Несколько вещей, которые я пробовал:

  1. async await на configureStore() - Ошибка пришла
  2. async await on App.js - Ошибка

PS: библиотеки, которые я использую: redux, redux-saga, response-router-dom, connected-response-router, axios

1 Ответ

0 голосов
/ 13 марта 2019

Один способ, который я понял:
Создайте отдельный компонент MyRouteWrapper, который будет возвращать маршруты на основе статуса isLoggedIn. Чтобы решить проблему, я прекращаю рендеринг маршрутов, пока автоматический вход не изменит состояние isLoggedIn.

Я установил состояние по умолчанию isLoggedIn на null. Теперь, если состояние null, MyRouteWrapper вернет пустую строку, и как только состояние изменится на 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;

Компонент, который обеспечивает возврат Route только при изменении состояния на true / false

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);

Это решило проблему.

Мне по-прежнему любопытно узнать, какие решения (лучше) у кого-то есть.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...