React Router とは?

Routerアプリのひな型作成

$ npx create-react-app my-router
$ cd my-router
$ npm install react-router-dom
$ npm install recoil
$ npm install react-hook-form
$ npm install @hookform/resolvers yup
 

[CAUTION] 多少のバージョン不整合があっても npm audit fix -force を実行しないこと!

こんなアプリを作った

router01.png

基盤画面とRouterの設定

index.js は変わらない

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import {RecoilRoot} from 'recoil';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RecoilRoot>
      <App />
    </RecoilRoot>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App.js に route を設定

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/App.js

import './App.css';
import {RouterProvider, Route, createBrowserRouter, createRoutesFromElements} from 'react-router-dom';
import './index.css';
import BaseLayout from './BaseLayout';
import TopPage from './TopPage';
import ArticlePage from './ArticlePage';
import EditPage from './EditPage';
import AboutPage from './AboutPage';
import NotFoundPage from './NotFoundPage';

const routes = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<BaseLayout/>}>
      <Route path="" element={<TopPage/>}/>
      <Route path="article/:id" element={<ArticlePage/>}/>
      <Route path="about" element={<AboutPage/>}/>
      <Route path="edit/:id" element={<EditPage/>}/>
      <Route path="*" element={<NotFoundPage/>}/>
    </Route>
  )
  // 第二引数を省略すると React Router の URL は、サイト直下 https://example.com/article になる
  // 第二引数で、ベースURLを指定すると basename下 https://example.com/myapp/article になる
  //, {basename: '/myapp'}
)

function App() {
  return (
    <>
      <RouterProvider router={routes} />
    </>
  );
}

export default App;

共通レイアウト

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/BaseLayout.js

import './App.css';
import MenuPage from './MenuPage';
import {Outlet} from 'react-router-dom';

export default function BaseLayout() {
    return (
        <div id="layoutContainer">
            <div className="header">
                Header
            </div>
            <div className="nav">
                <MenuPage/>
            </div>
            <div className="body">
                <Outlet/>
            </div>
            <div className="footer">
                Footer
            </div>
        </div>
    );
}

メニュー

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/MenuPage.js

import {NavLink} from 'react-router-dom';
import {useRecoilValue} from "recoil";
import {counterAtom} from "./logic/state";
import {articleListSelector} from "./logic/state";

export default function MenuPage() {
    const count = useRecoilValue(counterAtom);
    const articleList = useRecoilValue(articleListSelector);

    return (
        <>
            <ul>
                <li><NavLink to="/">Top</NavLink></li>
                <li><NavLink to="/about">About us</NavLink></li>
                <li><NavLink to="/edit/0">New Page</NavLink></li>
                {articleList.map(article => (
                    <li key={article.id}>
                        <NavLink to={`/article/${article.id}`}>
                            {article.title.substring(0, Math.min(article.title.length, 10))}
                        </NavLink>
                    </li>
                ))}
                <li><NavLink to="/error/123">Error Page</NavLink></li>
            </ul>
            <p align="center">COUNTER: {count}</p>
        </>
    );
}

単純な Router (Top⇔About Us)

エラーページ

コンポーネント間のデータの受け渡し1 (Recoil Atom)

recoilの有効化

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import {RecoilRoot} from 'recoil';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RecoilRoot>
      <App />
    </RecoilRoot>
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

recoilで管理するグローバル変数 (Atom)

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/logic/state.js

import {atom, atomFamily, selector, selectorFamily} from 'recoil';

// CAUTION: Don't miss spell of "key" and "default".
//
// If you incorrectly type dafault as "deault", the whole app will stop working (the browser shows white screen)
// and Recoil reports an incorrect following error message : 
// "A component suspended while responding to synchronous input. 
// This will cause the UI to be replaced with a loading indicator. 
// To fix, updates that suspend should be wrapped with startTransition."

export const counterAtom = atom({
    key: 'counterAtom',
    default: 0
});

export const idsAtom = atom({
    key: 'idsAtom',
    default: []
});

export const articleAtom = atomFamily({
    key: 'articleAtom',
    default: null
});

export const artilceSelector = selectorFamily({
  key: "artilceSelector",
  get:  (id) => ({ get }) => {
      const atom = get(articleAtom(id));
      return atom;
  },
  set: (id) => ({set}, item) => {
    set(articleAtom(id), item);
    set(idsAtom, ids => {
        if (ids.includes(id)) {
            return ids;
        }
        return [...ids, id];
    });
  }
});

export const articleListSelector = selector({
    key: 'articleListSelector',
    /** 
     * Get all articles.
     * @param get a function to get atom.
     */
    get: ({get}) => {
        const ids = get(idsAtom);
        return ids.map(id => get(articleAtom(id)));
    },
    /** 
     * Set all articles.
     * @param get a function to get atom.
     * @param set a function to sett atom.
     * @param reset a function to reset atom.
     */
    set: ({get, set, reset}, item) => {
        console.log(item);
        set(articleAtom(item.id), item);
        set(idsAtom, ids => [...ids, item.id]);
    }
});

atomの参照

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/MenuPage.js

import {NavLink} from 'react-router-dom';
import {useRecoilValue} from "recoil";
import {counterAtom} from "./logic/state";
import {articleListSelector} from "./logic/state";

export default function MenuPage() {
    const count = useRecoilValue(counterAtom);
    const articleList = useRecoilValue(articleListSelector);

    return (
        <>
            <ul>
                <li><NavLink to="/">Top</NavLink></li>
                <li><NavLink to="/about">About us</NavLink></li>
                <li><NavLink to="/edit/0">New Page</NavLink></li>
                {articleList.map(article => (
                    <li key={article.id}>
                        <NavLink to={`/article/${article.id}`}>
                            {article.title.substring(0, Math.min(article.title.length, 10))}
                        </NavLink>
                    </li>
                ))}
                <li><NavLink to="/error/123">Error Page</NavLink></li>
            </ul>
            <p align="center">COUNTER: {count}</p>
        </>
    );
}

atomの更新

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/TopPage.js

import {useRecoilState} from "recoil";
import {Link} from 'react-router-dom';
import {counterAtom} from "./logic/state";

export default function TopPage () {
    const [count, setCount] = useRecoilState(counterAtom);

    const handleClick = () => {
      setCount(count + 1);
    }
    return (
        <>
          This is Top Page.
          <br/>
          <button onClick={handleClick}>Count UP</button>
          <br/>
          <Link to="/about">About Us</Link>
        </>
    );
}

atom01.png

https://github.com/kagyuu/ReactExam/blob/main/04_react_router/my-router/src/AboutPage.js

import {useRecoilState} from "recoil";
import {Link} from 'react-router-dom';
import {counterAtom} from "./logic/state";

export default function AboutPage () {
  const [count, setCount] = useRecoilState(counterAtom);

  const handleClick = () => {
    setCount(count > 0 ? count - 1 : count);
  }

  return (
        <>
          This is About Page.
          <br/>
          <button onClick={handleClick}>Count DOWN</button>
          <br/>
          <Link to="/">Top</Link>
        </>
    );
}

atom02.png

コンポーネント間のデータの受け渡し2 (Recoil AtomFamily?)

以下未整理


HTML#React


添付ファイル: fileatom02.png 248件 [詳細] fileatom01.png 247件 [詳細] filetop.png 263件 [詳細] fileabout.png 272件 [詳細] filerouter01.png 273件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS   sitemap
Last-modified: 2024-02-08 (木) 23:13:56 (303d)
Short-URL: http://at-sushi.com/pukiwiki/index.php?cmd=s&k=d376c07e79
ISBN10
ISBN13
9784061426061