Daisuke.W
2022.06.15 フロントエンドLT会 - vol.7
Daisuke@React SolidJS大好き侍
(Twitter: @fullStackHaruno)
ある日、Qiitaの記事 「React大好き侍が、「もうSolidJSでいいじゃん…//」ってなったワケ。」 を読んで興味が湧いた
書き方やコードの見た目はReactそっくりだが、完全に別物だった
function App() {
const [count, setCount] = createSignal(0);
// countがゲッター(値の読み取り)
// setCountがセッター(値の書き込み)
return (
<button onClick={() => setCount(count() + 1)}>
Count: {count()}
</button>
);
}
return()内とcreateEffect()内のどこでゲッターが読みとられたのかを追跡して変更があれば更新する
1秒ごとにカウントアップする例
const App = () =>{
const [count, setCount] = createSignal(0);
// 1秒ごとに加算
const intervalID = setInterval(() => setCount(prev => prev + 1), 1000);
// コンポーネントが破棄される時にクリーンアップを実行
onCleanup(() => clearInterval(intervalID));
return <div>Count: {count()}</div>;
}
SolidJSはコンポーネントを再レンダリングしないので、setIntervalは一度だけ呼び出される
Reactで同じ書き方をした場合、際限なくsetIntervalを呼び出して暴走する
Counterコンポーネント側で分割代入をすると、countの追跡が途切れるため初期値0から変わらない
const Counter = ({ count }) => {
return <div>Count: {count}</div>; // 0
};
const App = () => {
const [count, setCount] = createSignal(0);
return (
<>
<Counter count={count()} />
<button onClick={() => setCount((prev) => prev + 1)}>加算</button>
</>
);
};
分割代入せずにprops.countとすることで追跡できる
const Counter = (props) => {
return <div>{props.count}</div> // 0,1,2...
}
propsのデフォルト値はmergePropsで設定できる
const Counter = (props) => {
const merged = mergeProps({ count: 0 }, props);
return <div>{merged.count}</div> // 0,1,2...
}
以下の定義方法はならpropsの個別設定ができる
const Counter = (props: { count }) => {...}
Reactで短絡評価を使って「<div>Loading...</div>
」を出力する場合、
<>
{
isLoading && <div>Loading...</div>
}
</>
SolidJSで同じことをする場合、ヘルパー関数のShowを用いる (whenがtrueならchildrenを表示、falseならfallbackの内容を表示)
<Show when={isLoading()} fallback={<div>記事一覧</div>}>
<div>Loading...</div>
</Show>
Reactで三項演算子を使うと…
<>
{
条件1 ? (
<Component1 />
) : 条件2 ? (
<Component2 />
) : 条件3 ? (
<Component3 />
) : (
<Component4 />
)
}
</>
SolidJSの場合、ヘルパー関数のSwitchとMatchを用いる (Switch内を上から順番に評価して、条件に一致するコンポーネントを一つ出力する)
return (
<Switch fallback={<Component4 />}>
<Match when={条件1} children={<Component1 />} />
<Match when={条件2} children={<Component2 />} />
<Match when={条件3} children={<Component3 />} />
</Switch>
);
この他にも、配列をイテレーションするForや、コンポーネントをオブジェクトにまとめて直接表示するDynamic等ヘルパーが用意されている
「yarn add --dev sass
」を実行するだけ
Viteのプラグイン(vite-plugin-pwa)を入れて、manifestを宣言するだけ
SASSやPWA化のヒントはSolidJS公式のtemplete集にある
SolidJSもReactのカスタムフックと同じ書き方でにロジックを分離することができる
function createTodo() {
const [count, setCount] = createSignal(0);
const handleInput = () => setCount((prev) => prev + 1);
return { count, handleInput };
}
const App1 = () =>{
const { count, handleInput } = createTodo();
return (...);
}
const App2 = () =>{
const { count, handleInput } = createTodo();
return (...);
}
スコープはそれぞれのコンポーネント内となる
createRootでラップすることでコンテキストとして扱える
function createTodo() {...}
const createTodoCtx = createRoot(createTodo)
const App1 = () =>{
const { count, handleInput } = createTodoCtx;
return (...);
}
const App2 = () =>{
const { count, handleInput } = createTodoCtx;
return (...);
}
App1で変化があるとApp2も変化する
変数を宣言して必要なタイミングでfocusを実行するだけ
let inputRef
createEffect(() => {
inputRef.focus();
...
});
return <input type="text" ref={inputRef}>
refを関数として扱うことも可能
// elはHTMLInputElement型
const inputRef = (el:HTMLInputElement) => {
createEffect(() => {
...
el.focus();
});
}
return <input type="text" ref={inputRef}>
ロジック分離する場合はこちらを使う必要がある