![[React Router] 直接ルーティングしていない孫コンポーネントからhistoryにアクセス可能にする(WithRouter)](https://devio2023-media.developers.io/wp-content/uploads/2019/01/react.jpg)
[React Router] 直接ルーティングしていない孫コンポーネントからhistoryにアクセス可能にする(WithRouter)
この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、CX事業本部 IoT事業部の若槻です。
今回は、React Router(react-router-dom)でWithRouterを使って、直接ルーティングしていない孫コンポーネントからhistoryにアクセス可能にしてみました。
やってみた
具体的にやりたいこととしてはこんな感じです。ルーティング先の子コンポーネント内で通常のタグコンポーネントとして使用されている孫コンポーネント内でhistoryにアクセスできるようにしてみます。
<親コンポーネント> ↓ ルーティング ←ここでprops.history.pushで値(state.userName)を渡す <子コンポーネント> ↓ 通常のタグコンポーネントとして使用 <孫コンポーネント> ←ここでstate.userNameにアクセスする
実装
次のようなコンポーネント構成で確認してみます。
- 親コンポーネント:
Home - 子コンポーネント:
MyProfile - 孫コンポーネント:
Header 
親コンポーネントHomeでは、props.history.pushで子コンポーネントMyProfileへのルーティングを行っています。その際にstate.userNameを渡しています。
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
const Home = (props: RouteComponentProps) => {
  return (
    <div>
      <h1>Home page</h1>
      <ul>
        <li>
          <button
            onClick={() => {
              props.history.push({
                pathname: '/profile',
                state: { userName: 'わかつき' }
              });
            }}
          >
            Go to my profile
          </button>
        </li>
      </ul>
    </div>
  );
};
export default Home;
MyProfileでは、孫コンポーネントHeaderを、ルーティングではなく、通常のタグコンポーネントとして使用しています。
import React from 'react';
import Header from './header/Header';
import { RouteComponentProps } from 'react-router-dom';
const MyProfile: React.FC<RouteComponentProps> = (
  props
) => {
  return (
    <div>
      <Header />
      <h1>This is my profile</h1>
      <ul>
        {props.location.state && (
          <li>
            <button
              onClick={() => {
                props.history.goBack();
              }}
            >
              Back
            </button>
          </li>
        )}
      </ul>
    </div>
  );
};
export default MyProfile;
孫コンポーネントHeaderです。props.location.state.userNameによりhistoryにアクセスするようにしていますが、この場合は正常に動きません。
import React from 'react';
import './Header.css';
import { RouteComponentProps } from 'react-router-dom';
type Props = RouteComponentProps<
  {},
  {},
  { userName: string }
>;
const Header: React.FC<Props> = (props) => {
  return (
    <div className="header">
      <h1>ヘッダー</h1>
      {props.location.state &&
        props.location.state.userName}
    </div>
  );
};
export default Header;
props.location.state.userNameへのアクセスがundefinedとなりエラーとなってしまいます。

MyProfile側でもHeaderの読み込みがエラーとなってしまいます。

そこでWithRouterを使用することにより、直接props.history.pushによるルーティングをしていないコンポーネントでもhistoryにアクセスできるようになります。
import React from 'react';
import './Header.css';
import {
  RouteComponentProps,
  withRouter
} from 'react-router-dom';
type Props = RouteComponentProps<
  {},
  {},
  { userName: string }
>;
const Header: React.FC<Props> = (props) => {
  return (
    <div className="header">
      <h1>ヘッダー</h1>
      {props.location.state &&
        props.location.state.userName}
    </div>
  );
};
export default withRouter(Header);
デモ
Home Page/でボタンをクリックすると、親コンポーネントHomeからのルーティング時にhistoryにより渡したuserNameに、孫コンポーネントHeaderからアクセスできています。
子コンポーネントMyProfileのページ/prifileに直接アクセスすると、ルーティングは行われていないので、HeaderコンポーネントからuserNameにはアクセスできず、表示はされません。
参考
- reactjs - What is withRouter for in react-router-dom? - Stack Overflow
 - react-router-domのwithRouter apiを使う場面 - code-log
 - React RouterのpropsをTypeScriptで型付けする | DevelopersIO
 
以上







