URL 更改但页面新页面未呈现 Ionic React ,具有历史记录的 IonReactRouter

URL changes but page new page is not rendered Ionic React , IonReactRouter with history

我正在使用 Ionic 5 与 React 和 Redux 编写一个应用程序。我尝试在成功登录后导航到新页面 /tabs/home。当我从后端获得成功响应时,我试图通过将新的 URL 推送到 react router history 道具上来做到这一点。这是有效的,因为它将 url 从 /login 更改为 /tabs/home 但仍显示登录页面。

index.tsx

import React from 'react';
import {render} from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import { store } from './helpers/store';
import { Router } from 'react-router';
import CreateBrowserHistory from 'history/createBrowserHistory';

export const history = CreateBrowserHistory();

render(
    <Provider store={store}>
        <Router history={history}>
             <App />           
        </Router>
    </Provider>,
    document.getElementById('root')
);

App.tsx

import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { history } from './index';
import { alertActions } from './actions/alert.actions';
import { Route } from 'react-router-dom';
import {
  IonApp,
} from '@ionic/react';
import { IonReactRouter } from '@ionic/react-router';
import { LoginPage } from './pages/Login';

import MainTabs from './pages/MainTabs';

function App() {
  const alert = useSelector(state => state.alert);
  const dispatch = useDispatch();

  useEffect(() => {
    history.listen((location, action) => {
      console.log(location);
      console.log(action);
      dispatch(alertActions.clear());
    });
  }, []);

  return (
    <IonApp>
        {/*
         // @ts-ignore*/}
        <IonReactRouter history={history}>
          <Route path="/tabs" component={MainTabs} />
          <Route path="/login" component={LoginPage} />
        </IonReactRouter>
    </IonApp>
  )
}

export default App;

Login.tsx

function LoginPage() {
  const [inputs, setInputs] = useState({
    username: '',
    password: ''
  });
  const [submitted, setSubmitted] = useState(false);
  const { username, password } = inputs;
  const loggingIn = useSelector(state => state.authentication.loggingIn);
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(userActions.logout());
  }, []);

  function handleChange(e) {
    const { name, value } = e.target;
    setInputs(inputs => ({ ...inputs, [name]: value }));
  }

  function handleSubmit(e) {
     e.preventDefault();

     setSubmitted(true);
     if (username && password) {
       dispatch(userActions.login(username, password));
     }
  }

  return (
    <IonPage id="login-page">
      <IonHeader>
        <IonToolbar>
          <IonButtons slot="start">
            <IonMenuButton></IonMenuButton>
          </IonButtons>
          <IonTitle>Login</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent>

        <form noValidate onSubmit={handleSubmit}>
          <IonList>
            <IonItem>
              <IonLabel position="stacked" color="primary">Username</IonLabel>
              <IonInput name="username" type="text" value={username} spellCheck={false} autocapitalize="off" onIonChange={handleChange} className={'form-control' + (submitted && !username ? ' is-invalid' : '')} required>
              </IonInput>
            </IonItem>

            <IonItem>
              <IonLabel position="stacked" color="primary">Password</IonLabel>
              <IonInput name="password" type="password" value={password} onIonChange={handleChange} className={'form-control' + (submitted && !password ? ' is-invalid' : '')} required>
              </IonInput>
            </IonItem>

          </IonList>

          <IonRow>
            <IonCol>
              {loggingIn && <span className="spinner-border spinner-border-sm mr-1"></span>}
              <IonButton type="submit" expand="block">Login</IonButton>
            </IonCol>
            <IonCol>
              <IonButton routerLink="/signup" color="light" expand="block">Signup</IonButton>
            </IonCol>
          </IonRow>
        </form>

      </IonContent>

    </IonPage>
  )
}

export { LoginPage };

登录操作

function login(username, password) {
    return dispatch => {
        dispatch(request({ username }));

        userService.login(username, password)
        .then(
            user => {
                dispatch(success(user));
                history.push('/tabs/home');
            },
            error => {
                dispatch(failure(error.toString()));
                dispatch(alertActions.error(error.toString()));
            }
        );
    };

    function request(user) { return { type: userConstants.LOGIN_REQUEST, user } }
    function success(user) { return { type: userConstants.LOGIN_SUCCESS, user } }
    function failure(error) { return { type: userConstants.LOGIN_FAILURE, error } }
}

只需在Route中加入exact属性即可解决

 <Route path="/tabs" exact component={MainTabs} />
 <Route path="/login" exact component={LoginPage} />

在你的代码中你有

<Route path "/tabs" component={MainTabs} />

什么时候应该

<Route path "/tabs/home" component={MainTabs} />

当您尝试使用 /tabs/home 推送您的历史记录时,没有为此指定路线,因此它不知道去哪里。

同样在你的索引文件中,我建议有一个像这样的额外路由

<Route exact path "/tabs" component={SomeComponent} />
<Route path "/tabs/home" component={MainTabs} />

以防万一您计划将多条路径附加到您的 /tabs 路线。

问题:

根据文档:

IonReactRouter 组件包装了来自 React Router 的传统 BrowserRouter 组件,并为应用程序设置路由。因此,使用 IonReactRouter 代替 BrowserRouter。您可以将任何道具传递给 IonReactRouter,它们将传递给基础 BrowserRouter.

我认为 BrowserRouter 不支持自定义 history,如您所见 BrowserRouter 支持此道具,那里没有 history 道具

basename
forceRefresh
getUserConfirmation
keyLength
children

history 道具可用于 Router

Issue on github


解决方案:

所以我为使用自定义历史记录做了以下更改并且它正在工作


index.js

import React from "react";
import { render } from "react-dom";
import App from "./App";
import { Provider } from "react-redux";
import { store } from "./helpers/store";

render(
  <Provider store={store}>
      <App />
  </Provider>,
  document.getElementById("root")
);

App.tsx

    <IonApp>
      <Router history={history}>
        {/* <IonReactRouter history={history}> */}
        <Route path="/tabs" component={MainTabs} />
        <Route exact path="/" component={LoginPage} />
        {/* </IonReactRouter> */}
      </Router>
    </IonApp>

你可以用这样的东西作为 hack,ref

import React from 'react'
import { History } from 'history'
import { useHistory } from 'react-router'

// Add custom property 'appHistory' to the global window object
declare global {
  interface Window { appHistory: History }
}

const MyApp: React.FC = () => {
  // Store the history object globally so we can access it outside of React components
  window.appHistory = useHistory()

  ...
}