Skip to content

React Router v6 でのナビゲーション(useHistory から useNavigate へ)

問題

React Router v6 では、以前のバージョンで使用されていた this.props.history.push()createBrowserHistory() が期待通りに動作しないという問題が発生します。URLは変更されますが、ページのコンポーネントが再レンダリングされず、ナビゲーションが完了しない現象が起こります。

具体的な問題点

jsx
// v5 では正常に動作したコード
this.props.history.push('/UserDashboard');

// v6 で試みたが失敗するコード
const history = createBrowserHistory();
history.push('/UserDashboard');

解決策

React Router v6 では、ナビゲーションの方法が大幅に変更されました。主な解決策は以下の通りです。

1. 関数コンポーネントでの useNavigate フック

関数コンポーネントでは、useNavigate フックを使用するのが標準的な方法です。

jsx
import { useNavigate } from 'react-router-dom';

function MyComponent() {
  const navigate = useNavigate();

  const handleSubmit = (event) => {
    event.preventDefault();
    // 送信ロジックなど...
    navigate('/UserDashboard');
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* フォーム内容 */}
      <button type="submit">送信</button>
    </form>
  );
}

2. クラスコンポーネントでの対応方法

クラスコンポーネントではフックが使用できないため、以下の方法で対応します。

方法 A: 高階コンポーネント(HOC)を作成

jsx
// withRouter.js
import React from 'react';
import { useNavigate } from 'react-router-dom';

const withRouter = (WrappedComponent) => {
  return (props) => {
    const navigate = useNavigate();
    return <WrappedComponent {...props} navigate={navigate} />;
  };
};

export default withRouter;
jsx
// クラスコンポーネントでの使用例
import React, { Component } from 'react';
import withRouter from './withRouter';

class MyClassComponent extends Component {
  handleClick = () => {
    this.props.navigate('/target-page');
  };

  render() {
    return (
      <button onClick={this.handleClick}>
        ページ遷移
      </button>
    );
  }
}

export default withRouter(MyClassComponent);

方法 B: ラッパーコンポーネントを使用

jsx
import React from 'react';
import { useNavigate } from 'react-router-dom';

const ElementWrapper = ({ routeElement: Element, ...props }) => {
  const navigate = useNavigate();
  return <Element navigate={navigate} {...props} />;
};

export default ElementWrapper;
jsx
// ルーティング設定での使用
<Route path="/users" element={<ElementWrapper routeElement={UserDashboard} />} />

3. コンポーネント外でのナビゲーション

Redux アクションや API 呼び出しなど、React コンポーネントの外でナビゲーションが必要な場合:

jsx
// navigation.js
let navigateRef;

export const setNavigate = (navigate) => {
  navigateRef = navigate;
};

export const navigateTo = (path) => {
  if (navigateRef) {
    navigateRef(path);
  }
};
jsx
// App.js や最上位コンポーネント
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { setNavigate } from './navigation';

function App() {
  const navigate = useNavigate();
  
  useEffect(() => {
    setNavigate(navigate);
  }, [navigate]);

  return (
    // アプリケーションのレイアウト
  );
}
jsx
// コンポーネント外での使用例
import { navigateTo } from './navigation';

axios.post('/api/login', data)
  .then(response => {
    navigateTo('/dashboard');
  });

注意点

この方法では、ナビゲーション関数が設定される前に navigateTo を呼び出さないように注意してください。最上位コンポーネントがマウントされた後にのみ使用可能です。

4. カスタムルーターの作成(上級者向け)

高度な制御が必要な場合は、カスタムルーターを作成することもできます。

jsx
import { useState, useLayoutEffect } from 'react';
import { Router } from 'react-router-dom';
import { BrowserHistory } from 'history';

const CustomRouter = ({ history, children }) => {
  const [state, setState] = useState({
    action: history.action,
    location: history.location,
  });

  useLayoutEffect(() => history.listen(setState), [history]);

  return (
    <Router
      location={state.location}
      navigationType={state.action}
      navigator={history}
    >
      {children}
    </Router>
  );
};

export default CustomRouter;

コード例

元の質問のコードを v6 スタイルで書き直すと以下のようになります。

jsx
import { useNavigate } from 'react-router-dom';

// 関数コンポーネント内で
const navigate = useNavigate();

const handleSubmit = (event) => {
  event.preventDefault();
  
  axios({
    method: "POST", 
    url: "http://localhost:3001/users/login", 
    data: this.state
  }).then((response) => {
    if (response.data.success === true && response.data.user.admin === false) {
      navigate("/users", {
        state: {
          Key: response.data.user
        }
      });
    } else if (response.statusCode === 401) {
      alert("Invalid username or password");
      window.location.reload(false);
    }
  });
};

まとめ

React Router v6 では、ナビゲーションの方法が以下のように変更されました:

  • useHistoryuseNavigate に変更
  • history.push(path)navigate(path) に変更
  • クラスコンポーネントでは HOC やラッパーを使用
  • コンポーネント外ではナビゲーション参照を共有

これらの変更はコードの簡素化と型安全性の向上を目的としており、適応することでより現代的な React コードベースを構築できます。