异步请求后如何在路由中重定向?

How to redirect in routes after async request?

我有这段代码,其中我使用 react-router-dom v6 进行路由

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      accounts: [],
    };
  }

  componentDidMount() {
    getAccounts().then(r => this.setState({ // async req
      accounts: r
    }));
  };

  render() {
    return (
      <Routes>
        <Route path="/" element={<AppLayout accounts={this.state.accounts} />}>
          <Route index element={<Navigate to={`/account/1`} />}/>
          <Route path="/account/:accountId" element={<TimelinePage/>}/>
          <Route path="*" element={<NotFoundPage/>}/>
        </Route>
      </Routes>
    );
  }
}

我想从主页重定向到 /accounts/${this.state.accounts[0]} 但数组 this.state.accounts 在加载 App 组件之前在异步请求后被填充,结果是我第一次呈现时,我得到至 <NotFoundPage/>。这是合乎逻辑的,但我如何才能先获取异步数据,然后才重定向?

临时解决了

<Route index element={<Navigate to={'/account/1'} />}/>`

但这并不能保证ID为1的元素永远存在于数组中


P.S. 数组 this.state.accounts 包含具有数字 属性 accountId 的对象。在 <TimelinePage/> 组件中,我使用 useParams() 获取帐户 ID,在 <AppLayout/> 组件中,我呈现数组的每个元素

使用 useNavigate 挂钩访问 navigate 函数并在获取帐户后发出命令式重定向。这里的问题是 App 是一个 class 组件,不能直接使用 React hooks。

选项是:

  1. App转换为函数组件。
  2. 创建自定义 withRouter HOC 以访问挂钩并将 navigate 作为道具注入。

App 非常简单,转换为函数组件是微不足道的。

import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';

const App = () => {
  const navigate = useNavigate();
  const [accounts, setAccounts] = useState([]);

  useEffect(() => {
    getAccounts()
      .then(accounts => {
        setAccounts(accounts);
        navigate("/account/1", { replace: true });
      });
  }, []);

  return (
    <Routes>
      <Route path="/" element={<AppLayout accounts={accounts} />}>
        <Route path="/account/:accountId" element={<TimelinePage />} />
        <Route path="*" element={<NotFoundPage />} />
      </Route>
    </Routes>
  );
}

如果 class 组件更复杂,创建自定义 withRouter HOC 并传入 navigate 作为道具可能会更容易。

import { useNavigate, ...other hooks... } from 'react-router-dom';

const withRouter = Component => props => {
  const navigate = useNavigate();
  ...other hooks...

  return (
    <Component
      {...props}
      navigate={navigate}
      ...other hooks...
    />
  );
};

应用程序

class App extends Component {
  state = {
    accounts: [],
  }

  componentDidMount() {
    const { navigate } = this.props;
    getAccounts()
      .then(accounts => {
        this.setState({ accounts });
        navigate("/account/1", { replace: true });
      });
  }

  render() {
    const { accounts } = this.state;
    return (
      <Routes>
        <Route path="/" element={<AppLayout accounts={accounts} />}>
          <Route path="/account/:accountId" element={<TimelinePage />} />
          <Route path="*" element={<NotFoundPage />} />
        </Route>
      </Routes>
    );
  }
}

export default withRouter(App);