vars.io

SolidJS


Trying out SolidJS

Reactivity in React and Solid

All major framework approaches to frontend reactivity allow you to build diagrams between state primitives and their derived or computed values. The idea is that when you update state then all subscribed components will be updated.

LibraryReactive PrimitiveDerived Value
ReactHooksMemo
SolidStore/SignalsMemo
RecoilAtomSelector
ReduxStoreSelector

In Solid, a store is a collection of signals in the form of an object or array.

Signals allow you to Read-Your-Writes

This React snippet is a common example of a beginner mistake as React cannot show you fresh values until re-running your component function.

function ReactExample() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    const elem = document.getElementById("count")
    console.log(`count: ${count}. dom: ${elem.textContent}`)
    setCount(x => x + 1)
    console.log(`state: ${count}. dom: ${elem.textContent}`)
  })

  return <div id="count">{count}</div>
}

In Solid createSignal will return a two element array of an accessor and a setter function, where the accessor can pull the latest value of the signal.

function SolidExample() {
  const [count, setCount] = createSignal(0)

  createEffect(() => {
    const elem = document.getElementById("count")
    console.log(`count: ${count()}. dom: ${elem.textContent}`)
    setCount(x => x + 1)
    console.log(`count: ${count()}. dom: ${elem.textContent}`)
  })

  return <div id="count">{count()}</div>
}

Solid’s reactivity is granular

Solid attempts to determine at compile-time which elements in your JSX tree are dependent on which signals, and it will leave behind a minimal runtime for granular mutation of your DOM when your state updates. In the example below, the elements containing time, mouse.x, and mouse.y can be updated independently from each other.

function SolidExample() {
  const [time, setTime] = createSignal(0)
  const [mouse, setMouse] = createStore({ x: 0, y: 0 })

  console.log("I run only once since I don't depend on any signal.")
  console.log("Updated time: ", time())

  onMount(() => {
    const id = setInterval(() => setTime(x => x + 1), 1000)
    onCleanup(() => clearInterval(id))
  })

  onMount(() => {
    const handler = e => setMouse({ x: e.clientX, y: e.clientY })
    document.addEventListener("mousemove", handler)
    onCleanup(() => document.removeEventListener("mousemove", handler))
  })

  return (
    <section>
      <div>Time: {time()}</div>
      <div>Mouse:
        <ul>
          <li>x: {mouse.x}</li>
          <li>y: {mouse.y}</li>
        </ul>
      </div>
    </section>
  )
}

In React, the entire <section> must be re-rendered in order to reflect changes to either time or mouse.

Note that I don’t have to call an accessor function to access the value of a the mouse store, but that’s only a syntactical convenience as Solid intercepts the object access and calls a getter function for you.