(めっちゃ短いけど、ちょいちょい調べるので)
useLayoutEffect は componentDidMount や componentDidUpdate と同じフェーズで実行されるということに注意してください
引用元: フック API リファレンス useLayoutEffect
逆に言えば、componentDidMount と componentDidUpdate は layout と paint の間に同期的に実行されるという意味
(めっちゃ短いけど、ちょいちょい調べるので)
useLayoutEffect は componentDidMount や componentDidUpdate と同じフェーズで実行されるということに注意してください
引用元: フック API リファレンス useLayoutEffect
逆に言えば、componentDidMount と componentDidUpdate は layout と paint の間に同期的に実行されるという意味
最近のYouTube君がよく使うこのUIパターン、名前ついてるのか気になってる
— クロパンダ (@le_panda_noir) 2022年8月12日
(フロート型で下スワイプで消せて上部にバーがついてるやつ) pic.twitter.com/EJeLQ8wSoS
↑これを CSS + JS で作りました。フロートカードと呼んでますが、正式名称は分かりません。半モーダルとか bottom drawer とか slidable drawer と呼ぶらしい?
JS をほぼ使わずに CSS の scroll-snap を使って実装しました。scroll-snap を使うと次のような実装ができます。
便利ですね。touchstart や touchmove のイベント量を監視してアレコレするよりよほどシンプルです。
scroll-snap を使うと、カルーセルのようにスクロールした際に特定の位置でピタッと止まるように吸着させられます。
scroll-snap は応用の幅があります。例えば、「スワイプで LIKE」「スワイプで削除」のような機能もカンタンに作れます。ほかの人が作ったデモ。便利ですね。
今回のフロートカードも scroll-snap 応用編に近いですよね。
これは、カード外がタップされたら overflow-y: hidden
をつけるようにして解決しました。 overflow-y: scroll
を消すというやり方だと iPhone の safari でうまく動作しなかったので…
これは、カードの上下にスナップポイントを用意してやることで解消しました。今回初めて scroll-snap 使ったので、思いつくまでに時間がかかりました。
実はいくつか未実装の箇所がある。
この辺りは素直に scroll-snap を使うだけだと難しい気がします。どうしたらできるんだろ。
初めにも張りましたが、動かせるデモはこちらです。ぜひ触ってみてください。結構わけわからないコードになってるので。
scroll-snap について知らなかったのでだいぶ勉強になりました。仕事でカルーセル実装するときに JS で頑張っていたけど、scroll-snap を使っていればもっと楽できたのでは…?とムチャクチャ思いました。
あと、StackBlitz でゴリゴリ初めて書きましたが、微妙にかゆいところに手が届かない感じですね…例えばこのあたり。
useState
を補完で出すと React.useState
になるimport * as React from 'react'
が消せない今回みたいにパッと動くものを作る用途では便利ですが、1時間以上書く場合は少しストレスですね。
みんなもフロートカードを作ってみよう!意外と苦戦するよ!
https://github.com/Fieldscope/flux-hooks を参考にしつつ、型をつけたり型推論が効きやすいように調整したりしてます。
export const useFluxStore = < TStore extends ReduceStore<TState, unknown>, TState = ReturnType<TStore['getState']> >( store: TStore, reducer: (store: TStore) => TState = store => store.getState(), deps: unknown[] = [] ): TState => { const [out, dispatch] = useReducer( (_, store) => reducer(store), reducer(store) ); useMemo(() => { dispatch(store); }, deps); useEffect(() => { const token = store.addListener(() => dispatch(store)); dispatch(store); return () => token.remove(); }, [store]); return out; };
使う方は非常にカンタンです。これだけ。
const state = useFluxStore(Store);
型推論がうまく働かなくなるので destructuring はしないでください。
const {foo: hoge, bar: fuga} = useFluxStore(Store); // hoge と fuga はどちらも any になってしまう
一旦変数に格納した後であれば destructuring しても問題ありません。
const store = useFluxStore(Store); const {foo: hoge, bar: fuga} = store; // これなら OK const hoge = useFluxStore(Store).foo; // これも問題なし
僕の TS 力ではこれが限界でした……
"再利用可能になった" はわかる。テストを書きやすいように書くと自然とそうなるから。でも、"再利用可能にしよう" がわからない。YAGNI(You ain't gonna need it)に反しているし、モジュールの安定度を上げる行為だからもっと慎重になるべきだ。そもそも、再利用する理由が同じでないのに再利用する危険性もある。
たとえば Button コンポーネントを考える。かなり汎用的な名前なので様々な場所で"使われてしまう"。結果、Button コンポーネントを変更しようとすると多くの場所に影響が出るようになる。こうなると Button は安易に変更ができない=安定度が高いコンポーネントとなる。
逆に、DangerButton コンポ―ネントを考える。このボタンは「キャンセル」や「削除」など取り消しがつかない操作をするときに使うボタンだ。このとき、DangerButton にデザイン変更が入ったとする。すると DangerButton を変えるだけで全箇所に変更が反映される。この場合は変更したい理由が同じだからOKだ(もちろん Button の場合も理由が同じであれば問題はないのだけれど、得てして Button は IconButton などに派生して使われがちなので理由が同じかどうかを判断するのが難しいことが多い)。
再利用可能にしなければ変更理由が同じコンポーネントをまとめることができないので、再利用可能に"結果としてなる"ことは多い。が、再利用したいから再利用可能にしようはちょっと待って欲しい。それは YAGNI に反しているのではないだろうか?
FlexiSpot e8を買ったのでレビュー
メチャ最高
とりあえず不満点はほぼない。価格も高すぎるって訳でもない。心なしか腰痛も治ってる気がする。
(腰痛が改善してもマイナスがゼロになるだけだから改善した感覚が得づらい)
買ってから気づいたが、立った状態と座った状態だと見やすいディスプレイの高さが微妙に違うのでモニターアームは必須かもしれない(我慢できないほどではない)
机の下にコード収納が一応あるのだが、これがネジ式になっていて結構めんどい。ネジを外してから落ちないように抑えつつネジを留める必要がある。配線をこまめに変える人はやめた方がいいかもしれない。
あと、デフォルトの天板にしたんだけど、角の部分の処理が甘くてチクチクする。そこまで致命的ではないけどたまに気になる。個体差があるかもなので、俺がハズレを引いただけかもしれない。
まあでもマジでそれくらいで不満点はほとんどない。