Open5

SolidJS Code Reading

Kazuki MatsudaKazuki Matsuda

SolidJS のリアクティビティシステムがどのように実装されているか気になるのでコード読む

https://www.solidjs.com/

実装見る前に公式サイトでも紹介されているこの辺りを読んだ方が良さそう。全て SolidJS 作者の Ryan Carniato 氏によって書かれた記事。

https://dev.to/ryansolid/a-hands-on-introduction-to-fine-grained-reactivity-3ndf

https://dev.to/ryansolid/building-a-reactive-library-from-scratch-1i0p

https://indepth.dev/posts/1289/solidjs-reactivity-to-rendering

Ryan Carniato 氏の記事はこのスクラップで読む

https://zenn.dev/kazukix/scraps/d68d584307b3b4

Kazuki MatsudaKazuki Matsuda

最初に SolidJS の主な特徴

  • Not 仮想 DOM
  • 高い パフォーマンス
  • React と似た構文
  • リアクティブな値のみを更新して再レンダリング (コンポーネント関数は 1 回のみ実行される)
Kazuki MatsudaKazuki Matsuda

createSignal 本体は packages/solid/src/reactive/signal.ts にある

export function createSignal<T>(): Signal<T | undefined>;
export function createSignal<T>(value: T, options?: SignalOptions<T>): Signal<T>;
export function createSignal<T>(
  value?: T,
  options?: SignalOptions<T | undefined>
): Signal<T | undefined> {
  options = options ? Object.assign({}, signalOptions, options) : signalOptions;

  const s: SignalState<T | undefined> = {
    value,
    observers: null,
    observerSlots: null,
    comparator: options.equals || undefined
  };

  if ("_SOLID_DEV_" && !options.internal) {
    if (options.name) s.name = options.name;
    registerGraph(s);
  }

  const setter: Setter<T | undefined> = (value?: unknown) => {
    if (typeof value === "function") {
      if (Transition && Transition.running && Transition.sources.has(s)) value = value(s.tValue);
      else value = value(s.value);
    }
    return writeSignal(s, value);
  };

  return [readSignal.bind(s), setter];
}

返り値である Signal の型。Getter 関数、Setter 関数のタプルを返す。Getter 関数 なことろが React の useState と異なる。

export type Accessor<T> = () => T;

export type Setter<T> = (undefined extends T ? () => undefined : {}) &
  (<U extends T>(value: (prev: T) => U) => U) &
  (<U extends T>(value: Exclude<U, Function>) => U) &
  (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);

export type Signal<T> = [get: Accessor<T>, set: Setter<T>];

SignalOptions 型

export interface BaseOptions {
  name?: string;
}

export interface EffectOptions extends BaseOptions {}

export interface MemoOptions<T> extends EffectOptions {
  equals?: false | ((prev: T, next: T) => boolean);
}

export interface SignalOptions<T> extends MemoOptions<T> {
  internal?: boolean;
}