Go to list of users who liked
Share on X(Twitter)
Share on Facebook
More than 3 years have passed since last update.
React大好き侍が、「もうSolidJSでいいじゃん...//」ってなったワケ。
Reactが好きです。
Reactが好きです。コンポーネントを関数として扱うのが好きです。
SolidJSはReactそっくりの書き心地(DX)を保ちつつ、Reactに足りない要素を兼ね備えた期待の新人です。
コードの比較
React
constCounter=()=>{const[count,setCount]=useState(0)useEffect(()=>{console.log(`Count:${count}`)},[count])return(<div><div>{count}</div><buttononClick={()=>setCount(prev=>prev+1)}>Add</button></div>)}SolidJS
constCounter=()=>{const[count,setCount]=createSignal(0)createEffect(()=>{console.log(`Count:${count()}`)})//第2引数はありません。SolidJSが勝手に依存している状態を感知してくれます。return(<div><div>{count()}</div><buttononClick={()=>setCount(prev=>prev+1)}>Add</button></div>)}そっくりですね。Reactを書いたことがある方なら容易に読めるはずです。
SolidJS最大のメリット
パフォーマンスです。もうばちくそ速いです。下のグラフによるとReactより約2倍も速いです。1なんならほぼVannilaJSと同じくらい速いです。
なにが違うのか
記述はそっくりなのになぜこんなにもパフォーマンスに差が出るのか。それは単純に、SolidJSとReactは根本的に別物だからです。具体的な違いを幾つかあげます。
仮想DOMがない
Reactといえば仮想DOMみたいなとこあるのでもう完全に別物ですね。SolidJSはjsxを本物のDOMにコンパイルしています。
状態の更新の副作用としてDOMを書き換えているらしいです。2
コンポーネント(関数)が一度しか呼ばれない
Reactのコンポーネント内に直でconsole.logを書くと、状態が更新されるたびに実行されます。SolidJSのコンポーネント内に同じくconsole.logを書いても、最初に関数が呼ばれる時しか実行されません。
これはなかなか大きな違いです。Reactデベロッパーは日々、「不必要な再レンダリングを防ぐ」という課題に頭を悩ませていますが、SolidJSにおいてはそもそも「再レンダリング」は発生しないのです。
SolidJSだから、できたこと。
こういった根本的な違いにより、SolidJSにのみ可能な芸当が存在します。
createSignalでやりたい放題
createSignalは、useStateとは違いHookではありません。React内に存在するHookのルールは適応されないのです。
コンポーネントの外に書くこともできますし、普通の関数内に書いたり、状況に応じて増やしたりもできます。
//コンポーネントの外に書いても普通に動作します。const[count,setCount]=createSignal(0)constCounter=()=>{return(<div><div>{count()}</div><buttononClick={()=>setCount(prev=>prev+1)}>Add</button></div>)}constCounter=()=>{//コンポーネント内の関数でも呼べます。この例だと、コンポーネント内に直接customHookを書いているような感じです。constcreateCounter=()=>{const[count,setCount]=createSignal(0)return{count,setCount}}constcounterOne=createCounter()constcounterTwo=createCounter()return(<div><div>{counterOne.count()}</div><buttononClick={()=>counterOne.setCount(prev=>prev+1)}>Add</button><div>{counterTwo.count()}</div><buttononClick={()=>counterTwo.setCount(prev=>prev+1)}>Add</button></div>)}constCounter=()=>{const[counters,setCounters]=createSignal([])//ボタンがクリックされるたびにカウンターが増えます。constaddCounter=()=>{const[count,setCount]=createSignal(0)setCounters(prev=>[...prev,{count,setCount}])}return(<div><Foreach={counters()}>{counter=>(<div><div>{counter.count()}</div><buttononClick={()=>counter.setCount(prev=>prev+1)}> Add</button></div>)}</For><buttononClick={addCounter}>Add Counter</button></div>)}context状態管理ライブラリもいらない
察しのいい方はお気づきでしょうが、createSignalがどこにでも書けるなら上記の2つは必要ありません。signals.tsのようなファイルに、グローバルな状態を保管すればよいのです。
import{createSignal}from'solid-js'exportconst[count,setCount]=createSignal(0)import{count,setCount}from'../signals'constCounter=()=>{return(<div><div><div>{count()}</div><buttononClick={()=>setCount(prev=>prev+1)}>Add</button></div></div>)}拍子抜けな程にシンプルに、グローバルな状態の管理ができますね。こういうのでいいんだよ。
6/10更新:createMemoなどの計算を含むグローバルな状態に関しては、createContextやcreateRootを使用します。(詳しくはこちら)createSignalは、Contextなどではオーバーキルになるようなシンプルな状態を管理するのに適しています。
なお、ssrを使用する場はcreateContextを使用します。
直でsetInterval
Reactの場合、こんな書き方をしたら再レンダーされるたびに新しくsetIntervalが登録されるので大変なことになります。一方SolidJSでは関数が一度しか呼ばれないので、トップレベルにsetIntervalを書いても問題ありません。
constCounter=()=>{const[count,setCount]=createSignal(0)constid=setInterval(()=>{setCount(prev=>prev+1)},1000)onCleanup(()=>{clearInterval(id)})return(<div><div>{count()}</div><buttononClick={()=>setCount(prev=>prev+1)}>Add</button></div>)}もうSolidJSでよくない?
まだReactが勝る部分はあるんですよ。Next.jsの存在とか、大きなコミュニティとか。
とはいっても、SolidJS版Next.jsは開発中とのことですし、コミュニティに関してもこれから育てていけばいいわけで。実際に海外では少しづつ注目を浴び始めていますしね3。
かくいう自分も、日本におけるSolidJSの認知度を高められたらいいなと思いこの記事を書きました。
「ぼくがかんがえたさいきょうのReact」がこのSolidJSです。ぜひお試しください。
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme
