Remixはこれから間違いなく伸びていくので、2025年頃が楽しみ
気が変わった。T3 Stackそのものは推して行くが、Theoさんの推す構成ではない。
aroundthedistance.hatenadiary.jp
フルスタックはReact前提に
RailsでフルスタックWebフレームワークの扉が開いた。Rails,Cake.Django等は、MVCのMCがメイン。Vが弱い。Vにテンプレートエンジンを使い、jQueryを使ってインタラクティブなUIを作っていたけど、もうそれが最適な時代ではない。ReactでUIをプログラミングする前提に立つと、ルーティングとrevalidateがルート単位で可能なFWが一番開発生産性が高くなる。可能であれば、ビジネスロジックもそこに入れて、だ。
Theoさん提唱のT3 Stackでは、Next.js + tRPCがコアのコンセプト。UIはNext.jsで書いて、スキーマを決めてtRPCでエンドポイントを生やし、サーバー通信を行ってUIを更新する。
Next.jsが得意な人であればこれでよいと思うが、私はNext.jsの状態管理ですごく手こずった。Next.jsは初学者にはかなり情報量が多いFWであり、Next.jsの決めた開発手法に強く従うことを求められるピーキーさが苦手。前提となる技術が抽象化されていて、なんかよくわからん状態に。
でも、ViewをReactで書いてTypeScriptでPOSTするとサーバーサイドのコードが呼び出されてUIの更新ができるなら、それがベスト。そして、Remixを見つけることが出来た。これが自分の欲しい物だった。
Remix推しポイント
クライアントとサーバーサイドが1つのファイルに書ける
やばくね??? コレが一番驚いた。tRPCのようなAPI定義も不要。フォームをPOSTしたらaction関数が実行される。
import { json } from "@remix-run/node"; import type { ActionFunctionArgs } from "@remix-run/node"; import { Form } from "@remix-run/react"; import { addTodo } from "~/todoStore"; export async function action({ request }: ActionFunctionArgs) { const formData = await request.formData(); const { todo } = Object.fromEntries(formData.entries()); await addTodo(todo); return json({ ok: true }); } export default function Index() { return ( <Form method="post"> <label htmlFor="todo">Item name</label> <input type="text" name="todo"/> <input type="text" name="description"/> <button>Add</button> </Form> ); }
このコードを書けるようになるまでがすごい大変なんだろうけど、開発体験最高に良かった。
Routingの設計が美しい
RemixのコアはVとC。特にC。React Routerによるクライアント側のルーティングに最高のスパイスが乗っている。Nested Routeなどがそれなんだけど、元々の設計として、ルートを起点に遷移するので、所謂サーバーサイドのミドルウェアを作るのが単純化できる。
Hooksをほとんど使わない
Formの実装がほとんど素のHTMLを変わらないのと、useEffectで副作用って所が、基本的に loader
関数で代替できるため。 useRef
でFormのDOMを触るなどは普通にあるし、チェックボックスに全部つけるみたいなのも、普通に標準のJavaScript書けばいいだけ。そこまでHooksでやるのめんどいだけだろっていう。
ただ、単純なFormでは表現できないようなものも当然ある。予約カレンダーをvisible:hiddenで持ってポップアップして表示する系のやつとか、トグルで開いた時に初めてデータ取ってくる系のやつとか。そういうのは・・・Hooks!
Hooksで処理すべきところと、Remixに委ねていいところが、実に明確で気持ちよい。これなら戦えるって思った。
CSRが出来ないので、Remixを使う時はBFF(実質Node.jsのアプリケーションサーバ)が必要になる。BFFおけないですっていうCSR前提なら、そもそもNext.jsでもなんでもなくて、Reactで書けば良いんじゃないのとも。
残念なことに「フルスタックフレームワーク」だから、バックエンドまできちんと書いたことがないと旨味が少ない。業務系システムはすごいRemixが向いていると思う。100画面ぐらいすぐ作れそう。やっと、自分が目指す形でWebアプリケーションが作れそう!!!嬉しい。
2024.01.15 追記
CSRが出来ないので、Remixを使う時はBFF(実質Node.jsのアプリケーションサーバ)が必要になる。
嘘でーーーす!! RemixがSPAモードのサポートを開始しました!! CSR作りたいなら素のReactよりRemix採用したほうが良いかもよ!!
npx prisma generate したのにPrismaClientがanyになったら
VSCodeの Reload Windowで解決した。。。ようです。。。60分返して...
Pythonで小数点の計算をする時、strでキャストすべし...
まじかよ...
decimalモジュールに記載がある通り、float型ではなくstr型にすることで回避できます。 ですので、実装時にはstr型にキャストしたほうが良いと思います。
>>> from decimal import Decimal, ROUND_HALF_UP >>> (Decimal(1750) * Decimal(0.43)).quantize(Decimal("0"), rounding=ROUND_HALF_UP) Decimal(752)
四捨五入出来てない・・・
>>> from decimal import Decimal, ROUND_HALF_UP >>> (Decimal(1750) * Decimal(str(0.43))).quantize(Decimal("0"), rounding=ROUND_HALF_UP) Decimal(753)
YES YES YES!
Dartも同じようなことがあった気がする。Python以外にもfloat to Decimalじゃなくて、str to Decimalにしろってやつ。752.4999999999999999 とかになっちゃうんだよね、前者だと。
WordPressの検索機能のカスタマイズ
post_type
を追加したいという要件だった。これで瞬殺。
<?php function custom_search_query($query) { if (!is_admin() && $query->is_search) { $query->set('s', $query->get('s')); $query->set('post_type', ["post", "page", "YOUR_POST_TYPE"]); } } add_filter('pre_get_posts', 'custom_search_query');
WP_Queryで指定できるものは何でも指定できるようなので、meta_dataやカスタムフィールドなんかも全然行けそー
FlutterのFirebase MessaingにおけるonTokenRefreshがよくわからない
このAPIすげー微妙な気がする。使い所がわからない。毎回 getToken
でデバイストークンをぶん投げてええんちゃうって思う。
端末が変わる、アプリの再インストールでは自動的に新しいデバイストークンが採択されるから、onTokenRefreshが呼び出されることがない。このメソッド Stream
を購読するものだから、Firebase側からのPUSHがないと動かない。
考えられるユースケースは、デバイストークンをアプリの操作で削除した場合。deleteToken
したら Stream
で新しいトークンが渡されて、それをサービスに投げるまでがワンセットみたいな仕様以外に、使い所が全然ないと思う。
起動時にgetToken
した値をバックエンドにぶん投げて、ユーザーと紐づけているトークンリストに該当していないならappendするべき。で、デバイストークンでPUSH通知を送ると失敗したトークンのリストがもらえるのでDELETEして、生きてるトークンだけ保持すれば良い。
これでいいのだ。
FlaskのBluePrintで404を指定することは出来ない
Flaskの仕様で、404のエラーハンドラーを blueprint
毎に定義することが出来ず、ちょっとハマった。
以下の公式にあるように、Flaskのアプリケーションそのものが、404になるURLがリクエストされた時にどのblueprintのエラーハンドラーを呼び出していいかわからんって書いてある。つまり、404と405はグローバルに定義しろってことらしい。
This is because the blueprint does not “own” a certain URL space, so the application instance has no way of knowing which blueprint error handler it should run if given an invalid URL.
Modular Applications with Blueprints — Flask Documentation (2.3.x)
@app.errorhandler(404) @app.errorhandler(405) def _handle_api_error(ex): if request.path.startswith('/api/'): return jsonify(error=str(ex)), ex.code else: return ex
初めてFlask触ったのが、2013年とかで、当時のバージョンは0.10だった。今は2.3か。それでも全然コアのアーキテクチャは変わらない。Flaskに学ぶことは多かった。