- 投稿日:
react-routerでRouteを切る実装例
確認環境
Env | Ver |
---|---|
React | 17.0.1 |
react-router-dom | 5.2.0 |
TypeScript | 4.1.3 |
サンプルコード
index.tsx
- ここに大元のルーティングを実装
import React from 'react';
import ReactDOM from 'react-dom';
import './index.scss';
import { BrowserRouter } from 'react-router-dom';
import { RootRoute } from './routes/RootRoute';
// ドメインルート以外にReactを設置する場合に必要
// e.g. `https://www.example.com/react/` に設置する場合 `/react` を設定
const basename = process.env.REACT_APP_BASE_NAME ?? undefined;
ReactDOM.render(
<React.StrictMode>
{/* `useHistory()` 用のDIラッパー */}
<BrowserRouter basename={basename}>
{/* ルーティング設定をまとめたコンポーネント */}
<RootRoute />
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
AppRoute.ts
- ルーティングの定義、これがあると後々の管理が楽
- スプレッド演算子で
Route
に対して展開できるように記述しておく
export const AppRoute = {
// React のルート定義
root: {
path: '/',
key: '/',
exact: true, // 完全一致
component: App,
},
// 404 定義
notfound: {
component: NotFound,
},
// `/foo` 以下のルーティング定義
foo: {
// `/foo` のルート定義
root: {
path: '/foo',
key: '/foo',
component: FooRoute,
},
// 以下、 `/foo` 配下のページのルーティング定義
hoge: {
path: '/foo/hoge',
key: '/foo/hoge',
component: HogePage,
},
piyo: {
path: '/foo/piyo',
key: '/foo/piyo',
component: PiyoPage,
},
},
};
RootRoute.tsx
- 各階層の基底ルーティング管理
export const RootRoute = () => {
return (
{/* `Route` は `Switch` で囲む */}
<Switch>
{/* root */}
<Route {...AppRoute.root} />
{/* foo */}
<Route {...AppRoute.foo.root} />
{/* bar */}
<Route {...AppRoute.bar.root} />
{/* 404 */}
<Route {...AppRoute.notfound} />
</Switch>
);
};
FooRoute.tsx
/foo
階層配下の管理用
export const FooRoute = () => {
return (
{/*
`Context` は `Switch` の外に出す
`Switch` の中に `Context` を書くと更に `Switch` でネストする必要がある
*/}
<FooContext.Provider value={FooContextDefault}>
<Switch>
{/* eslint-disable-next-line array-callback-return */}
{Object.entries(AppRoute.foo).map(([idx, route]) => {
if (idx !== 'root') {
/* `'root'` 以外の全ノードを展開 */
return <Route {...route} />;
}
})}
{/* 404 */}
<Route {...AppRoute.notfound} />
</Switch>
</FooContext.Provider>
);
};
- 投稿日:
PHPでClassをrequire
せずに使えるやつ
Laravelのrequest()
とかがどうやって呼ばれてるのかを調べていくうちに辿り着いたのでメモがてら
Class
は簡単に読み込めるけど、Function
は一筋縄ではいかなさそう
ついでにClass
に好き勝手プロパティ増やせることも発見した
autoloaderを実装
spl_autoload_registerを使う。Laravelはcomposerが上手いことやってくれてるっぽかった
<?
function regist() {
spl_autoload_register(function() {
require './Hoge.php';
require './Piyo.php';
});
}
使いたいクラス
適当に用意
<?
class Hoge {
public $hoge;
}
<?
class Piyo {
public $piyo;
}
autoloaderを呼んで使う
ついでに好き勝手にプロパティも生やす
<?
require './bootstrap.php';
// ここでクラスをautoloadする
regist();
// どこからも呼んでないけど
$h = new Hoge;
// 生やせる
$h->fuga = "aaa";
$h->hogepiyo = "bbb";
// 使える
$p = new Piyo;
$p->fuga = "aaa";
$p->hogepiyo = "bbb";
var_dump($h, $p);
/*
結果
class Hoge#2 (3) {
public $hoge =>
NULL
public $fuga =>
string(3) "aaa"
public $hogepiyo =>
string(3) "bbb"
}
class Piyo#3 (3) {
public $piyo =>
NULL
public $fuga =>
string(3) "aaa"
public $hogepiyo =>
string(3) "bbb"
}
*/
- 投稿日:
手順
composer init
でプロジェクトを作成composer require [パッケージ名]
でパッケージを入れる- 名前空間を作る場合、
composer.jspn
に以下のセクションを作成- リファレンス: getcomposer.org
"autoload": {
"psr-4": {
"App\\": "src/App" // 名前空間と対応するパス
},
"classmap": [
"src/App" // クラスを読み込むディレクトリのルート
]
}
autoload
やclass_alias
などを設定するbootstrap.php
を適当に作成- この
bootstrap.php
はエントリポイントからrequire_once
などで読み込無事で有効化する
- この
phpunit
を組み込む場合composer require --dev phpunit/phpunit
でインストールcomposer.json
に以下のセクションを作成
"scripts": {
"test": [
"phpunit --bootstrap bootstrap.php test"
]
}
- 投稿日:
確認環境
Env | Ver |
---|---|
Node.js | 12.18.3 |
Jest | 26.4.2 |
やりたいこと
以下の実装のときに、parentFunc()
を呼んだ時にchildFunc()
が呼ばれることをテストしたい
function parentFunc() {
console.log('called parentFunc');
childFunc('XXXX');
}
function childFunc(param) {
console.log(`called childFunc ${param}`);
}
各ケース紹介
Case1 そもそも構文がおかしい
テストするためには関数をexportする必要あるが、愚直過ぎて構文的に実行不能になるケース
exports.parentFunc = () => {
console.log('called parentFunc');
// childFuncは別モジュールなので呼べない
childFunc('XXXX');
};
exports.childFunc = (param) => {
console.log(`called childFunc ${param}`);
};
Case2 スコープ違いでテストが失敗する
この実装を実行すると期待通り動作するので、一見すると大丈夫そうに見える
function parentFunc() {
console.log('called parentFunc');
childFunc('XXXX');
}
function childFunc(param) {
console.log(`called childFunc ${param}`);
}
module.exports = { parentFunc, childFunc };
しかしこのテストを流すと失敗する
これはparentFunc()
が呼び出すchildFunc()
が下記case2
の中にないため
parentFunc()
のスコープ内にchildFunc()
がいないことが原因
const case2 = require('./case2');
// こうするとjest.spyOn()の第一引数を満たせないので落ちる
// const { parentFunc, childFunc } = require('./case2');
describe('inside call test', function () {
it('parentFunc', function () {
const spy = jest.spyOn(case2, 'parentFunc');
case2.parentFunc();
expect(spy).toHaveBeenCalled();
});
it('childFunc', function () {
const spy = jest.spyOn(case2, 'childFunc');
case2.parentFunc();
// childFuncはcase2に属していないため呼ばれない
expect(spy).toHaveBeenCalled();
});
});
Case3 テストが成功するケース
const parentFunc = () => {
console.log('called parentFunc');
// parentFunc()の中にchildオブジェクトを注入することで、
// jestがchildFunc()を認識できるようにする
child.childFunc('XXXX');
};
const child = {
childFunc: (param) => {
console.log(`called childFunc ${param}`);
},
};
// childFuncでなく、childオブジェクトをexportするのが味噌
module.exports = { parentFunc, child };
const case3 = require('./case3');
describe('inside call test', function () {
it('parentFunc', function () {
const spy = jest.spyOn(case3, 'parentFunc');
case3.parentFunc();
expect(spy).toHaveBeenCalled();
});
it('childFunc', function () {
// 注入している側のオブジェクトを参照する
const spy = jest.spyOn(case3.child, 'childFunc');
case3.parentFunc();
// child.childFuncはcase3に属しているため呼ばれる
expect(spy).toHaveBeenCalled();
});
});
- 投稿日:
パターンを思い付く都度に更新される予定。
yyyy-MM-dd HH-mm-ssをyyyy-m-dに変換
s/(\d{4})-0?(\d{1,2})-0?(\d{1,2}).+/$1-$2-$3/
3桁区切りカンマ
s/(\d)(?=(\d{3})+(?!\d))/$1,/g
直後に\d{4}でなく、\d{3}のパターンのある\dをキャプチャし、カンマを付与
ネストプロパティの2段目(bar)以降を配列に置換
s/(?<=\$request->.+?)->((?<!\$request->)(?<=->)([a-z_]+))+/['$2']/gi
- まず
(?<=\$request->.+?)
はキャプチャされず、この次に->
があるのが前提、->は削除するので先読み・後読み・キャプチャはしない - 次に式などで文章が両辺にあることを考慮して、文字列内に
\$request->
が再度含まれないことを(?<!\$request->)
として条件にする- これを入れてないと一行中の一文と解釈されるので、別の文と解釈させるため
- 次にプロパティ名の前には
->
が来るので、それを(?<=->)
として条件付けする - 最後に
([a-z_]+)
でプロパティ名であろうものを指定する - 2-4の繰り返し(ルートにある2個目のカッコ)に対して置換を掛けることでプロパティ名を配列に置換できる
- 但し1で指定している
->
単体は繰り返し対象外なのに繰り返し適用されているが、この理由はよくわかってない- 恐らく単純に
(?<=\$request->.+?)
の後ろにある->
が全部消えている?
- 恐らく単純に
// source text
$request->foo
$request->foo;
$request->foo->bar;
$request->foo['bar'];
$request->foo->bar->fb->fBfb;
$arr = [
'bar' => $request->foo->bar
];
if ($request->foo->bar->fb= $request->foo->bar->fb) {
$request->foo->bar->fb = $request->foo->bar->fb;
}
// replaced text
$request->foo
$request->foo;
$request->foo['bar'];
$request->foo['bar'];
$request->foo['bar']['fb']['fBfb'];
$arr = [
'bar' => $request->foo['bar']
];
if ($request->foo['bar']['fb']= $request->foo['bar']['fb']) {
$request->foo['bar']['fb'] = $request->foo['bar']['fb'];
}
但しメソッドは対象外にしたい場合
s/(?<=\$request->.+?)->((?<!\$request->)(?<=->(?![a-z_]+\(\)))([a-z_]+))+/['$2']/gi
->
の後に来るメソッド構文を除外するために、(?![a-z_]+())
を追加(?<=->(?![a-z_]+()))
とすることで->
の後に来る(?![a-z_]+())
を除外しつつ、->
の後に来る([a-z_]+)
をキャプチャできる
// source text
$request->foo
$request->foo;
$request->foo->bar;
$request->foo['bar'];
$request->foo->fooBar();
$request->foo->bar->fooBar();
$arr = [
'bar' => $request->foo->bar
];
if ($request->foo->bar->fb= $request->foo->bar->fb) {
$request->foo->bar->fb = $request->foo->bar->fb;
}
// replaced text
$request->foo
$request->foo;
$request->foo['bar'];
$request->foo['bar'];
$request->foo->fooBar();
$request->foo['bar']->fooBar();
$arr = [
'bar' => $request->foo['bar']
];
if ($request->foo['bar']['fb']= $request->foo['bar']['fb']) {
$request->foo['bar']['fb'] = $request->foo['bar']['fb'];
}