<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Solid-Js on Jonathan's Blog</title><link>https://jonathan-frere.com/tags/solid-js/</link><description>Recent content in Solid-Js on Jonathan's Blog</description><generator>Hugo</generator><language>en-gb</language><copyright>This work is licensed under CC BY-SA 4.0</copyright><lastBuildDate>Tue, 27 May 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://jonathan-frere.com/tags/solid-js/index.xml" rel="self" type="application/rss+xml"/><item><title>Side Effects in SolidJS</title><link>https://jonathan-frere.com/posts/side-effects-in-solidjs/</link><pubDate>Tue, 27 May 2025 00:00:00 +0000</pubDate><guid>https://jonathan-frere.com/posts/side-effects-in-solidjs/</guid><description>&lt;p>If you’re used to React, you’re probably used to using &lt;code>useEffect&lt;/code> to run side-effectful code, such as integrating with third-party libraries or setting up &lt;code>MutationObserver&lt;/code>s&lt;sup id="fnref:1">&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref">1&lt;/a>&lt;/sup>. SolidJS also has &lt;code>createEffect&lt;/code>, which works very similarly to &lt;code>useEffect&lt;/code>, but it also has some other functions, and I’ve noticed that people getting started with SolidJS often aren’t sure which function to use for any given side-effects.&lt;/p>
&lt;p>If you’re in that boat, then here is the answer in tabular form:&lt;/p></description><content:encoded><![CDATA[<p>If you’re used to React, you’re probably used to using <code>useEffect</code> to run side-effectful code, such as integrating with third-party libraries or setting up <code>MutationObserver</code>s<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.  SolidJS also has <code>createEffect</code>, which works very similarly to <code>useEffect</code>, but it also has some other functions, and I’ve noticed that people getting started with SolidJS often aren’t sure which function to use for any given side-effects.</p>
<p>If you’re in that boat, then here is the answer in tabular form:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right"></th>
          <th style="text-align: center">runs immediately</th>
          <th style="text-align: center">runs on mount</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right"><strong>is reactive<br>(runs multiple times)</strong></td>
          <td style="text-align: center"><code>createRenderEffect</code></td>
          <td style="text-align: center"><code>createEffect</code></td>
      </tr>
      <tr>
          <td style="text-align: right"><strong>is not reactive<br>(only runs once)</strong></td>
          <td style="text-align: center">no function needed</td>
          <td style="text-align: center"><code>onMount</code></td>
      </tr>
  </tbody>
</table>
<p>In this post, I’ll unpack this table and explain what it means in a bit more detail.</p>
<h2 id="what-are-side-effects">
  <a href="#what-are-side-effects">What are side effects?</a>
</h2>

<p>Side effects in this context is code that:</p>
<ul>
<li>interacts with the page (i.e. isn’t deriving new values from existing inputs)</li>
<li>isn’t handled by the framework itself (e.g. adding <code>onClick</code> handlers to a button via JSX, or using async/suspense).</li>
</ul>
<p>So for the purposes of this blog post, something like this might be a side effect:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="c1">// attach a global event listener from
</span></span></span><span class="line"><span class="cl"><span class="c1">// inside a particular component
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nb">document</span><span class="p">.</span><span class="nx">addEventHandler</span><span class="p">(</span><span class="s2">&#34;keydown&#34;</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">});</span>
</span></span></code></pre></div><p>But these probably aren’t side effects:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="c1">// derive the `fullName()` signal by
</span></span></span><span class="line"><span class="cl"><span class="c1">// combining a `firstName()` and `lastName()`
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">fullName</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">firstName</span><span class="p">()</span> <span class="o">+</span> <span class="s2">&#34; &#34;</span> <span class="o">+</span> <span class="nx">lastName</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// attach an event listener to a particular
</span></span></span><span class="line"><span class="cl"><span class="c1">// element using JSX
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">&lt;</span><span class="nt">input</span> <span class="na">onKeyDown</span><span class="o">=</span><span class="p">{()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}}</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// this does have side effects, but for the purposes of
</span></span></span><span class="line"><span class="cl"><span class="c1">// this post, those side effects are handled by SolidJS
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="p">[</span><span class="nx">resource</span><span class="p">]</span> <span class="o">=</span> <span class="nx">createResource</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">fetch</span><span class="p">(</span><span class="cm">/* ... */</span><span class="p">));</span>
</span></span></code></pre></div><h2 id="are-my-side-effects-reactive">
  <a href="#are-my-side-effects-reactive">Are my side effects reactive?</a>
</h2>

<p>There are broadly two types of side effect:</p>
<ul>
<li>Side effects that run once over a component’s entire lifecycle — i.e. some code that should run once when each instance of the component is first rendered, and then cleaned up when the component is unmounted, but never between.  These side effects are <em>not reactive</em>.</li>
<li>Side effects that run multiple times whenever an input changes — i.e. code that will regularly be rerun during the lifetime of the component<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</li>
</ul>
<p>For example, the global <code>keydown</code> handler from before will probably only need to run once: we set the handler when the component is mounted, and then remove it when we’re ready to clean the component up.  However, the classic <a href="https://overreacted.io/making-setinterval-declarative-with-react-hooks/"><code>useInterval</code></a> hook needs to use reactive side-effects: whenever the input parameters change, we need to clean up the old <code>setInterval</code> handler and create a new one.</p>
<h2 id="when-should-my-side-effects-run">
  <a href="#when-should-my-side-effects-run">When should my side effects run?</a>
</h2>

<p>SolidJS’s side effects can be triggered at two different times:</p>
<ul>
<li>Immediately, as the component is being created</li>
<li>Later, once the component has been mounted to the DOM and is ready to be used</li>
</ul>
<p>Some things only make sense once the component has been mounted: any code that needs to access <a href="https://docs.solidjs.com/concepts/refs"><code>ref</code></a>, for example, should run after the element has been created and attached to the DOM<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>.</p>
<p>Some things are not so clear-cut though.  For example <code>setInterval</code> doesn’t require any access to the DOM or to any particular elements, so it might make sense to put it in the “run immediately” category.  In practice, however, <code>setInterval</code> should normally only run once the component has been mounted.  Why?</p>
<p>The answer is server-side rendering.  When a component is rendered server-side, the DOM is never mounted per-se, so side effects that run only once mounted are never run.  However side effects that run immediately <em>are</em> run, because they will probably affect how the component will be rendered.  In fact, that’s normally the point of having them run immediately in the first place, so they can affect rendering!</p>
<p>If we put <code>setInterval</code> in the “run immediately” category, then it will be run on the server.  But the render code will never wait for the interval handler to actually start, it’ll just carry on generating HTML and sending it to the client.  And once the HTML has been generated, the component will be unmounted, the <code>setInterval</code> handler will be cleared, and the code in it probably won’t have even had the chance to run once, let alone affect how the code gets rendered<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>.</p>
<p>This won’t break anything (as long as the interval is correctly cleared), but it is a useless call to <code>setInterval</code>, and a useless resource that needs to be allocated and cleaned up at some point.  Therefore, if you’ve got a side effect and you’re sure it won’t affect rendering, consider running it only once the DOM has been mounted.</p>
<h2 id="bringing-it-together-as-a-table">
  <a href="#bringing-it-together-as-a-table">Bringing it together as a table</a>
</h2>

<p>If we combine the two questions above, we end up with four different cases, as shown in the table I showed earlier:</p>
<table>
  <thead>
      <tr>
          <th style="text-align: right"></th>
          <th style="text-align: center">runs immediately</th>
          <th style="text-align: center">runs on mount</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: right"><strong>is reactive<br>(runs multiple times)</strong></td>
          <td style="text-align: center"><code>createRenderEffect</code></td>
          <td style="text-align: center"><code>createEffect</code></td>
      </tr>
      <tr>
          <td style="text-align: right"><strong>is not reactive<br>(only runs once)</strong></td>
          <td style="text-align: center">no function needed</td>
          <td style="text-align: center"><code>onMount</code></td>
      </tr>
  </tbody>
</table>
<p>To finish up, let’s look at those four cases individually.</p>
<ul>
<li><code>createEffect</code>: The first run always happens after the component is mounted.  After that, if a signal used in the body of the effect changes, the effect will be rerun.  If no signals are used in the body, then this behaves the same as <code>onMount</code>.</li>
<li><code>onMount</code>: As the name says, this also happens after the component is mounted, but it is <em>not</em> reactive.  This is good for side effects that only need to run once that use the DOM.</li>
<li><code>createRenderEffect</code>: Reactively, this behaves the same as <code>createEffect</code>, but the first run will happen <em>immediately</em> (i.e. synchronously before the <code>createRenderEffect</code> is completed).  Similarly to <code>createEffect</code>, if no signals are used in the body of the effect, then this behaves the same as running the code in the body of the component.</li>
<li><em>no function needed</em>: Remember that the body of your component will only get called once during the component’s lifetime, so non-reactive side-effects that need to run immediately (such as instantiating a third-party library, or using <code>createUniqueId</code>) can be called as part of that body.</li>
</ul>
<h2 id="addendum-oncleanup">
  <a href="#addendum-oncleanup">Addendum: <code>onCleanup</code></a>
</h2>

<p>A lot of side effects require some sort of cleanup to happen later — for example, starting a <code>setInterval</code> timer requires cleaning up that timer when it’s no longer needed.  For that, SolidJS provides the <code>onCleanup</code> function.</p>
<p>As a rule of thumb, <code>onCleanup</code> can be called wherever there is a scope that would get cleaned up at some point.  For example, once a component is no longer being rendered, it needs to be cleaned up and removed from the DOM.  By calling <code>onCleanup</code> in the component body, you can add an extra callback that will be called when that happens.</p>
<p>For reactive effects (<code>createRenderEffect</code>/<code>createEffect</code>), calling <code>onCleanup</code> inside the effect callback will schedule a function to run when the effect is re-run, i.e. when a signal triggers the effect to restart, all <code>onCleanup</code> functions will get called, and then the new effect is run.</p>
<p>In general, put your <code>onCleanup</code> calls inside the function you use to create your side effect in the first place.  For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-tsx" data-lang="tsx"><span class="line"><span class="cl"><span class="c1">// NO!  Don&#39;t do this!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">onMount</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/* some side effect */</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="nx">onCleanup</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/* some clean up */</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ----
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="c1">// YES!  Do this!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">onMount</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="cm">/* some side effect */</span>
</span></span><span class="line"><span class="cl">  <span class="nx">onCleanup</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="cm">/* some clean up */</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>This is because generally you only want the <code>onCleanup</code> to run if the side effect was called in the first place.  As mentioned earlier, functions like <code>onMount</code> and <code>createEffect</code> will not run during SSR, but if the <code>onCleanup</code> isn’t inside one of those functions, then it will still be called during SSR, doing unexpected things.</p>
<h2 id="addendum-avoiding-effects">
  <a href="#addendum-avoiding-effects">Addendum: Avoiding effects</a>
</h2>

<p>At the start, I made the comparison to React’s <code>useEffect</code>, and in a footnote pointed out that in modern React, <code>useEffect</code> is generally discouraged — there’s often a better way.</p>
<p>In SolidJS, <code>createEffect</code> doesn’t behave in quite such surprising ways as <code>useEffect</code>, thanks in large part to the automatic dependency tracking that means that the effect normally re-runs when you’d expect it to re-run.  However, it’s still a good idea to look out for cases where you can replace <code>createEffect</code> with a better option.  When I look at <code>createEffect</code> calls, these are the things I’m looking out for:</p>
<ul>
<li>Is a signal setter being used in the body of the effect?  If so, the effect can almost certainly be replaced by <code>createMemo</code> or another form of computed signal.</li>
<li>Is an async function being called in the effect?  If so, it might be worth using <code>createResource</code> (or <code>createAsync</code> from Solid Router) instead, as this will handle some of the boilerplate for you, as well as integrating with the suspense mechanism.</li>
<li>Is the effect setting an event handler on a DOM element?  If so, use the <code>on*</code> handlers in JSX instead.  This seems obvious, but I’ve written this sort of code before, only to realise later that I’d missed an obvious and easier solution.</li>
<li>Is the function body reactive and/or would the effect work better if it was run immediately?  As discussed in this article, other functions for running side effects are available.</li>
<li>Is there a function that already handles this case in a <a href="https://primitives.solidjs.community/">Solid Primitives</a> package, or can you write your own wrapper around the effect to encapsulate it a bit better?  This one isn’t really about getting rid of effects, but rather hiding them somewhere else.  But encapsulation is good, and the Solid Primitives functions are generally useful, even if you’re just looking at the source code to get a hint of how to do something.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>You’re probably also used to people telling you not to use <code>useEffect</code> if you can avoid it.  That’s often good advice in SolidJS as well, although perhaps not to the same extent — I’ll discuss that a bit at the end of this post.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Comparison with React time again: React does <em>not</em> explicitly distinguish between these two cases.  <code>useEffect</code> is always reactive, albeit reactive based on the explicit deps array (unlike SolidJS where dependency tracking happens automatically).  However, the “run once” case is usually modelled by an empty deps array (i.e. <code>[]</code>), and the “run many times” case is modelled by adding dependencies to that array.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Note that code that <em>sets up</em> refs should run immediately, and not later.  This distinction is very rarely important — as a general rule, if you’re accessing a ref, you’ll want the code doing that to run after the DOM has been mounted.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>My inner pedant wants you to know that with async rendering/suspense, it is possible to manufacture situations where rendering will wait until <code>setInterval</code> has been called at least once.  I can’t think of any good reason why you should do this, but technically this paragraph isn’t quite true.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>