$ 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 を実行しないこと!
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();
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;
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>
);
}
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>
</>
);
}
a.active { color: Red; font-weight: bold; }
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>
</>
);
}
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>
</>
);
}
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;
import {Link, useParams} from 'react-router-dom';
export default function NotFoundPage () {
const {'*': paths} = useParams();
return (
<>
Sorry, PATH <b>{paths}</b> not found.
<br/>
<Link to="/">Top</Link>
</>
);
}
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();
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]);
}
});
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>
</>
);
}
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>
</>
);
}
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>
</>
);
}