<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: c0d3t3k</title>
    <description>The latest articles on DEV Community by c0d3t3k (@c0d3t3k).</description>
    <link>https://dev.to/c0d3t3k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F501049%2F4a6565e6-b2b1-4aaf-8dad-ae21c404fb97.png</url>
      <title>DEV Community: c0d3t3k</title>
      <link>https://dev.to/c0d3t3k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/c0d3t3k"/>
    <language>en</language>
    <item>
      <title>Blitz.js + React Three Fiber == React Three Blitz?!</title>
      <dc:creator>c0d3t3k</dc:creator>
      <pubDate>Wed, 24 Feb 2021 17:36:22 +0000</pubDate>
      <link>https://dev.to/c0d3t3k/blitz-js-react-three-fiber-react-three-blitz-ii3</link>
      <guid>https://dev.to/c0d3t3k/blitz-js-react-three-fiber-react-three-blitz-ii3</guid>
      <description>&lt;h1&gt;
  
  
  Blitz.js + React Three Fiber ==&amp;gt; React Three Blitz?
&lt;/h1&gt;

&lt;p&gt;In order to celebrate the recent &lt;a href="https://twitter.com/flybayer/status/1362048912476016642" rel="noopener noreferrer"&gt;Blitz.js promotion to formal Beta&lt;/a&gt;, &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1362048912476016642-823" src="https://platform.twitter.com/embed/Tweet.html?id=1362048912476016642"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1362048912476016642-823');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1362048912476016642&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;we thought it might be fun to see how it integrates with one of our favorite frameworks, &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;React Three Fiber&lt;/a&gt;. Introducing &lt;strong&gt;&lt;a href="https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;react-three-blitz&lt;/a&gt;&lt;/strong&gt;!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;a href="https://blitzjs.com/" rel="noopener noreferrer"&gt;Blitz.js&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;If you haven't seen it yet, you owe it to yourself to checkout one of &lt;a href="https://twitter.com/flybayer" rel="noopener noreferrer"&gt;Brandon Bayer&lt;/a&gt;'s (the founder and chief evangelist of Blitz.js) very thorough &lt;a href="https://youtu.be/UHyx8MtCVVk" rel="noopener noreferrer"&gt;video introductions&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/UHyx8MtCVVk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We are intrigued by &lt;strong&gt;blitz.js&lt;/strong&gt; because it offers a compelling, uniquely integrated (&lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails-like&lt;/a&gt;?) monolithic, full stack solution built using top tier open source components (React, &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;, &lt;a href="https://www.prisma.io/" rel="noopener noreferrer"&gt;Prisma ORM&lt;/a&gt;, &lt;a href="https://react-query.tanstack.com/" rel="noopener noreferrer"&gt;React Query&lt;/a&gt;, &lt;a href="http://www.passportjs.org/" rel="noopener noreferrer"&gt;Passport.js&lt;/a&gt; &lt;a href="http://www.passportjs.org/packages/" rel="noopener noreferrer"&gt;Auth Strategies&lt;/a&gt;,etc.). &lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt;?
&lt;/h2&gt;

&lt;p&gt;To put it simply, &lt;code&gt;r3f&lt;/code&gt; a &lt;a href="https://reactjs.org/docs/codebase-overview.html#renderers" rel="noopener noreferrer"&gt;React renderer&lt;/a&gt; for &lt;a href="https://threejs.org/" rel="noopener noreferrer"&gt;THREE.js&lt;/a&gt; for the web and &lt;a href="https://reactnative.dev/" rel="noopener noreferrer"&gt;react-native&lt;/a&gt;. &lt;strong&gt;THREE.js&lt;/strong&gt; has been a gamechanger but we really feel that &lt;a href="https://twitter.com/0xca0a" rel="noopener noreferrer"&gt;@0xca0a&lt;/a&gt;/&lt;a href="https://github.com/drcmda" rel="noopener noreferrer"&gt;@drcmda&lt;/a&gt; &lt;a href="https://github.com/pmndrs/react-three-fiber/graphs/contributors" rel="noopener noreferrer"&gt;et al&lt;/a&gt;'s &lt;strong&gt;react-three-fiber&lt;/strong&gt; [and the &lt;a href="https://github.com/pmndrs/drei" rel="noopener noreferrer"&gt;Drei&lt;/a&gt; utilites!] takes it to the next level by wrapping THREE.js primitives, etc. to keep all of our 3D web dev concise and performant.&lt;/p&gt;

&lt;p&gt;A lot of credit for this integration idea goes to &lt;a href="https://twitter.com/onirenaud" rel="noopener noreferrer"&gt;@onireanud&lt;/a&gt; &lt;a href="https://github.com/pmndrs/react-three-next/graphs/contributors" rel="noopener noreferrer"&gt;et al&lt;/a&gt; and his &lt;a href="https://github.com/pmndrs" rel="noopener noreferrer"&gt;@pmndrs&lt;/a&gt; umbrella creation &lt;a href="https://github.com/pmndrs/react-three-next" rel="noopener noreferrer"&gt;react-three-next&lt;/a&gt;. This excellent project introduces a compelling pattern, especially for integrating r3f and &lt;strong&gt;next.js&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Specifically, &lt;code&gt;react-three-next&lt;/code&gt; uses a unified layout model containing a THREE.js/r3f canvas and a react DOM container overlaid on top of each other. A rendering filter is used to seperate HTML and r3f components and render them in the appropriate container.&lt;/p&gt;

&lt;p&gt;Below is a quick rundown of changes we added/changed to get our 3D adventure started:&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix/Circumvent SSR using &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/blitz.config.js" rel="noopener noreferrer"&gt;next-transpile-modules&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Three.js&lt;/strong&gt;, &lt;strong&gt;&lt;a href="https://github.com/pmndrs/drei" rel="noopener noreferrer"&gt;Drei&lt;/a&gt;&lt;/strong&gt; etc. don't play well with &lt;a href="https://en.wikipedia.org/wiki/Server-side_scripting" rel="noopener noreferrer"&gt;SSR&lt;/a&gt; so we needed a way to pre-transpile these libraries.&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/blitz.config.js" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv9vxi6b3frkqcb9qk9u7.png" alt="blitz.config.js" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/_canvas.tsx" rel="noopener noreferrer"&gt;Reusable Canvas&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Our goal here was to leverage a "reusable" Canvas ala &lt;a href="https://github.com/pmndrs/react-three-next" rel="noopener noreferrer"&gt;react-three-next&lt;/a&gt;. All "3D" elements (hero, logo, etc. in our case) would render as children of this element. We also included some configurable niceties like &lt;a href="https://github.com/RenaudRohlinger/r3f-perf" rel="noopener noreferrer"&gt;r3f-perf&lt;/a&gt;, &lt;a href="https://threejs.org/docs/#examples/en/controls/OrbitControls" rel="noopener noreferrer"&gt;OrbitControls&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/_canvas.tsx" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fivnzc7x3khrko03knu47.png" alt="app/core/layouts/_canvas.tsx" width="800" height="563"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/_dom.tsx" rel="noopener noreferrer"&gt;DOM/HTML Component Container&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Also similarly to &lt;strong&gt;react-three-next&lt;/strong&gt; we used a wrapper for all non 3D or plain "dom" elements.&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/_dom.tsx" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F44lymgn774c5ewkpuvdl.png" alt="app/core/layouts/_dom.tsx" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/Layout.tsx#L29" rel="noopener noreferrer"&gt;SplitApp&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Then, we use this component either aggregate the 3D and non-3D components, if 3D components are present. Or, just output a standard non-canvas component wrapper.k&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/Layout.tsx#L29" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1z1jwdr72mhawg92iv3.png" alt="app/core/layouts/Layout.tsx#L29" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/Layout.tsx" rel="noopener noreferrer"&gt;Layout&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As described earlier, this component is used to create separate arrays of r3f and HTML based on the presence of a "key". Using these arrays we determine whether or not to display the Canvas.&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/core/layouts/Layout.tsx" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmxc6yaxvow4ik5ul48pp.png" alt="app/core/layouts/Layout.tsx" width="800" height="588"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/pages/index.tsx" rel="noopener noreferrer"&gt;Index&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Finally, we import our 3D component and disable SSR. Then, we use the &lt;strong&gt;Layout&lt;/strong&gt; component from above and mark each &lt;strong&gt;3D&lt;/strong&gt; with a &lt;em&gt;key&lt;/em&gt; (i.e. 'r3f', etc.) to let the render know we will be presenting a &lt;strong&gt;r3f&lt;/strong&gt; component.&lt;br&gt;
&lt;a href="https://github.com/c0d3t3k/react-three-blitz/blob/master/app/pages/index.tsx" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuh3mkxe8qcvyn1iwvyxa.png" alt="app/pages/index.tsx" width="800" height="809"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;react-three-blitz&lt;/a&gt; starter is definitely a work-in-progress. In fact, we don't have all the coolness ported yet from &lt;a href="https://github.com/pmndrs/react-three-next#mount_fuji-features" rel="noopener noreferrer"&gt;react-three-next&lt;/a&gt; (i.e. transitions, webpack customization, etc.). Also, there in-progress &lt;a href="https://github.com/pmndrs/react-three-fiber/issues/1004" rel="noopener noreferrer"&gt;r3f issue&lt;/a&gt; preventing us from sharing a canvas across routes to optimize navigation responsiveness.&lt;/p&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/pmndrs/react-three-fiber/issues/1004" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Allow opting out of forceContextLoss()
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1004&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/robonyong" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars.githubusercontent.com%2Fu%2F10537463%3Fv%3D4" alt="robonyong avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/robonyong" rel="noopener noreferrer"&gt;robonyong&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/pmndrs/react-three-fiber/issues/1004" rel="noopener noreferrer"&gt;&lt;time&gt;Feb 11, 2021&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;I'm working on a page where I would like to reuse/manage webgl contexts outside of the specific components that use Canvas. would it be possible to add a way to opt out of Canvas calling &lt;code&gt;forceContextLoss()&lt;/code&gt; on unmount?&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/pmndrs/react-three-fiber/issues/1004" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Nevertheless, we encourage you to give &lt;a href="https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;react-three-blitz&lt;/a&gt; a spin!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gitpod.io/#https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgitpod.io%2Fbutton%2Fopen-in-gitpod.svg" alt="Open in Gitpod" width="160" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;-c0d3t3k&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/c0d3t3k" rel="noopener noreferrer"&gt;
        c0d3t3k
      &lt;/a&gt; / &lt;a href="https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;
        react-three-blitz
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      React Three Fiber experimental starter template powered by Blitz.js
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;&lt;a href="https://blitzjs.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8db6ed10e8f18ce88bcb722869d50b71776c23304b9c83bc700abefe3f5761c9/68747470733a2f2f692e6962622e636f2f6b4a36633344772f5233426c69747a2e676966" alt="Blitz.js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dev.to/c0d3t3k/blitz-js-react-three-fiber-react-three-blitz-ii3" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/5dffb9a0dbb9f0b75f1f1926a632e0f4b35539dfd4fadeef1bc50087a908fc55/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6465762e746f2d61727469636c652d6c6967687467726579" alt="dev.to"&gt;&lt;/a&gt; .&lt;a href="https://www.youtube.com/watch?v=UHyx8MtCVVk" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5e1ab5bd20ae8a4287839549ba29c0440fed96d455501c3191a28e2b6fdbe840/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f696e74726f2d626c69747a2d626c756576696f6c6574" alt="Intro Video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://discord.blitzjs.com" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/cde306f60b60920beb3e31e74d5530cf106a718ca98aac75db043b8c1accfeef/68747470733a2f2f696d672e736869656c64732e696f2f646973636f72642f3734303039303736383136343635313030383f7374796c653d666c617426636f6c6f72413d30303030303026636f6c6f72423d303030303030266c6162656c3d646973636f7264266c6f676f3d646973636f7264266c6f676f436f6c6f723d666666666666" alt="Discord Shield"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://gitpod.io/#https://github.com/c0d3t3k/react-three-blitz" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b04f5659467d23b5109ba935a40c00decd264eea25c22d50a118021349eea94f/68747470733a2f2f676974706f642e696f2f627574746f6e2f6f70656e2d696e2d676974706f642e737667" alt="Open in Gitpod"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;&lt;strong&gt;react-three-blitz&lt;/strong&gt;&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://blitzjs.com/docs/get-started" rel="nofollow noopener noreferrer"&gt;Blitz.js&lt;/a&gt;&lt;/strong&gt; experiment/starter influenced heavily by &lt;a href="https://twitter.com/onirenaud" rel="nofollow noopener noreferrer"&gt;@onireanud&lt;/a&gt; &lt;a href="https://github.com/pmndrs/react-three-next/graphs/contributors" rel="noopener noreferrer"&gt;et al&lt;/a&gt; &lt;strong&gt;&lt;a href="https://github.com/pmndrs/react-three-next#mount_fuji-features" rel="noopener noreferrer"&gt;react-three-next&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Getting Started&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Run your app in the development mode.&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;yarn
yarn dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Open &lt;a href="http://localhost:3000" rel="nofollow noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; with your browser to see the result.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Interesting Changes&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;List of delta's from the base &lt;strong&gt;&lt;a href="https://blitzjs.com/docs/get-started" rel="nofollow noopener noreferrer"&gt;Blitz.js&lt;/a&gt;&lt;/strong&gt; generated app&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;react-three-blitz
├── app/
│   ├── core/
|   │   └── components/ 
│   │   |   └── DarkMode.tsx
│   │   |   └── Logo.tsx
│   │   └── layouts/
│   │       └── _canvas.tsx
│   │       └── _dom.tsx
│   │       └── Layout.tsx
│   ├── pages/
│   │   ├── _app.tsx
│   │   └── index.tsx
│   │   └── hero.tsx
│   ├── api/
│   ├── auth/
│       ├── pages/
│       ├── login3d.tsx
│       └── signup3d.tsx
├── blitz.config.js
├── tsconfig.json
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;#shouldersofgiants&lt;/strong&gt; shoutouts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/pmndrs/react-three-next" rel="noopener noreferrer"&gt;react-three-next&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pmndrs/react-three-fiber/graphs/contributors" rel="noopener noreferrer"&gt;blitz.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/pmndrs/react-three-fiber/graphs/contributors" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/c0d3t3k/react-three-blitz" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>blitzjs</category>
      <category>nextjs</category>
      <category>r3f</category>
      <category>react</category>
    </item>
    <item>
      <title>Jotai, now with Optics</title>
      <dc:creator>c0d3t3k</dc:creator>
      <pubDate>Sat, 31 Oct 2020 14:05:48 +0000</pubDate>
      <link>https://dev.to/c0d3t3k/jotai-now-with-optics-29ld</link>
      <guid>https://dev.to/c0d3t3k/jotai-now-with-optics-29ld</guid>
      <description>&lt;p&gt;More and more &lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;functional paradigms&lt;/a&gt; have been finding their way into our contracting work. This really accelerated when we started using &lt;a href="https://reactjs.org/docs/hooks-overview.html" rel="noopener noreferrer"&gt;React Hooks&lt;/a&gt; a while back. In fact, back in the day, we were tasked to convert a legacy &lt;a href="https://angular.io/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; &lt;a href="https://threejs.org/" rel="noopener noreferrer"&gt;ThreeJS&lt;/a&gt; project we had written earlier to React / &lt;a href=""&gt;react-three-fiber&lt;/a&gt; for performance, ease of maintenance, etc. Given the increasing complexity, we wanted a more atomic, composable state management system (of course this was before &lt;a href="https://recoiljs.org/" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; had been introduced). After some due diligence, we settled on &lt;a href="https://github.com/grammarly/focal" rel="noopener noreferrer"&gt;Grammarly's Focal&lt;/a&gt;. This library, although a bit older, is powerful and introduced us to the intriguing &lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;FP&lt;/a&gt; concepts of &lt;a href="https://degoes.net/articles/fp-glossary#:~:text=Optics" rel="noopener noreferrer"&gt;Optics&lt;/a&gt;, &lt;a href="https://degoes.net/articles/fp-glossary#:~:text=Lenses" rel="noopener noreferrer"&gt;Lenses&lt;/a&gt;, etc&lt;/p&gt;

&lt;p&gt;Fast forward to now and we are learning more about &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;, a &lt;a href="https://recoiljs.org/" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; alternative from &lt;a href="https://github.com/pmndrs" rel="noopener noreferrer"&gt;Poimandres&lt;/a&gt; ( creators of &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt;, etc.). Needless to say, we were very excited when we stumbled upon &lt;a href="https://github.com/pmndrs/jotai/issues/44" rel="noopener noreferrer"&gt;Jotai Issue #44&lt;/a&gt;, a discussion concerning focusable atoms started by &lt;a href="https://github.com/merisbahti" rel="noopener noreferrer"&gt;Meris Bahtijaragic&lt;/a&gt; and the compelling work that resulted, &lt;a href="https://github.com/c0d3t3k/jotai-optics" rel="noopener noreferrer"&gt;jotai-optics&lt;/a&gt;. This code wraps another library we have been very intrigued by as of late, &lt;a href="https://github.com/akheron/optics-ts" rel="noopener noreferrer"&gt;optics-ts&lt;/a&gt; which provides a whole new level of typesafe, functional goodness.&lt;/p&gt;

&lt;p&gt;Now, if the concept of &lt;a href="https://degoes.net/articles/fp-glossary#:~:text=Optics" rel="noopener noreferrer"&gt;Optics&lt;/a&gt; is new to you, there are some excellent introductions in the context of functional programming. One such concise example is &lt;a href="https://medium.com/@gcanti/introduction-to-optics-lenses-and-prisms-3230e73bfcfe" rel="noopener noreferrer"&gt;@gcanti's article&lt;/a&gt; on lenses and prisms, and there are &lt;a href="https://www.google.com/search?q=optics+introduction+functional+programming" rel="noopener noreferrer"&gt;plenty more&lt;/a&gt;. &lt;a href="https://degoes.net/articles/fp-glossary" rel="noopener noreferrer"&gt;John DeGoes' Glossary of Functional Programming&lt;/a&gt; will also help with any new &lt;a href="https://en.wikipedia.org/wiki/Functional_programming" rel="noopener noreferrer"&gt;FP&lt;/a&gt; vocabulary.  However, our humble goal here is to provide more of a practical (vs academic) example.&lt;/p&gt;

&lt;p&gt;In order to explore this new functionality, we will use an existing Recoil example. We will not only convert to &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;, but also add some extra functionality to soft introduce some benefits of &lt;a href="https://github.com/merisbahti/jotai-optics" rel="noopener noreferrer"&gt;&lt;code&gt;jotai-optics&lt;/code&gt;&lt;/a&gt; (and &lt;a href="https://github.com/akheron/optics-ts" rel="noopener noreferrer"&gt;&lt;code&gt;optics-ts&lt;/code&gt;&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;For this exercise, we thought it might be fun to upgrade &lt;a href="https://github.com/diogo405" rel="noopener noreferrer"&gt;Diogo Gancalves'&lt;/a&gt; cool &lt;a href="https://github.com/diogo405/joeflix" rel="noopener noreferrer"&gt;Joeflix&lt;/a&gt; app to &lt;a href="https://github.com/c0d3t3k/joeflix/tree/jotai-optics" rel="noopener noreferrer"&gt;JotaiFlix&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feyzk8f1lhz4et5fh9c6f.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Feyzk8f1lhz4et5fh9c6f.gif" alt="Alt Text" width="720" height="405"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's get started.&lt;/p&gt;

&lt;p&gt;First, we we need to replace &lt;code&gt;RecoilRoot&lt;/code&gt; with the Jotai &lt;code&gt;Provider&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.js exceprt&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;//import {RecoilRoot} from 'recoil'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jotai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="cm"&gt;/* &amp;lt;RecoilRoot&amp;gt; */&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;JotaiDebugger&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;FeedbackPopup&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will add some Favorites and History to the UI. This will give us some specific user generated state our Optics can act upon. In order to accomplish this, we need to first create some Jotai Atoms that will store this state. While we are at it, we will include some default values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state.js excerpt&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;historyAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;62286&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fear the Walking Dead&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;What did the world look like as it was transformin… the end of the world, will answer that question.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/58PON1OrnBiX6CqEHgeWKVwrCn6.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;528085&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2067&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/5UkzNSOK561c2QRy2Zr4AkADzLT.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;favoritesAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;590223&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Love and Monsters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/lA5fOBqTOQBQ1s9lEYYPmNXoYLi.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;movie&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;76479&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Boys&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A group of vigilantes known informally as “The Boys” set out to take down corrupt superheroes with no more than blue-collar grit and a willingness to fight dirty.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;banner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mGVrXeIjyecj6TKmwPVpHlscEmw.jpg&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we need a function that determines if a given movie/show is already contained in either the &lt;em&gt;Favorites&lt;/em&gt; or &lt;em&gt;History&lt;/em&gt; collection. If it is present, it removes it, if not present it adds it.&lt;/p&gt;

&lt;p&gt;Lets talk about what is happening here. In short, we use a &lt;a href="https://github.com/c0d3t3k/jotai-optic" rel="noopener noreferrer"&gt;jotai-optics&lt;/a&gt; wrapped &lt;a href="https://github.com/akheron/optics-ts" rel="noopener noreferrer"&gt;optics-ts&lt;/a&gt; &lt;a href="https://degoes.net/articles/fp-glossary#:~:text=Isomorphism" rel="noopener noreferrer"&gt;isomorphism&lt;/a&gt; to transform the internally passed atom collection passed by the outer &lt;code&gt;focus&lt;/code&gt; call.&lt;/p&gt;

&lt;p&gt;Because we need to track both the current and converted boolean value, we create a wrapper object within the optic that has two properties (&lt;code&gt;contained&lt;/code&gt; and &lt;code&gt;value&lt;/code&gt;). The &lt;code&gt;contained&lt;/code&gt; property tracks the boolean output of the optic and the &lt;code&gt;value&lt;/code&gt; property tracks the array that potentially contains the specified item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// optics.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;containsOptic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;O&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;optic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iso&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="c1"&gt;// Lens that is isomorphically converting an array given an item &lt;/span&gt;
            &lt;span class="c1"&gt;// to a boolean determining whether the array contains that item.&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; 
                &lt;span class="na"&gt;contained&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;currentItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contained&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contained&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contained&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To keep things relatively simple in the &lt;code&gt;BigTile.js&lt;/code&gt;, &lt;code&gt;Tile.js&lt;/code&gt; and &lt;code&gt;Hero.js&lt;/code&gt; files we call our &lt;code&gt;containsOptic&lt;/code&gt; factory function above to instantiate an optic that will provide not only History and Favorite state, but a way to easily set it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tile.js excerpt&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// https://github.com/merisbahti/jotai-optics&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isInHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsInHistory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="nf"&gt;useAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;historyAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;optic&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;optic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;containsOptic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isFavorite&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setIsFavorite&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
        &lt;span class="nf"&gt;useAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;favoritesAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;optic&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;optic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;containsOptic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we'll add some &lt;a href="https://react-icons.github.io/react-icons/" rel="noopener noreferrer"&gt;icon&lt;/a&gt; buttons to call the respective setters created by the &lt;code&gt;jotai-optics&lt;/code&gt; &lt;code&gt;focus&lt;/code&gt; method above, to mutate the Favorites and History state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Continued Tile.js excerpt&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleFavorites&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsFavorite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isFavorite&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playMedia&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setIsInHistory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isInHistory&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tile__play&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;toggleFavorites&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isFavorite&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AiFillHeart&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AiOutlineHeart&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tile__play&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;playMedia&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tile__icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../images/streamline-icon-controls-play@15x15.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that about does it!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvvkx21eebv85fjxf1fz4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvvkx21eebv85fjxf1fz4.gif" alt="Alt Text" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Using an &lt;a href="https://degoes.net/articles/fp-glossary#:~:text=Optics" rel="noopener noreferrer"&gt;optics&lt;/a&gt; based implementation ensures that state mutations can be modular and concise.&lt;/li&gt;
&lt;li&gt;With the &lt;a href="https://github.com/akheron" rel="noopener noreferrer"&gt;@akeron&lt;/a&gt;'s &lt;a href="https://github.com/akheron/optics-ts" rel="noopener noreferrer"&gt;&lt;code&gt;optics-ts&lt;/code&gt;&lt;/a&gt; library, powerful optics can be constructed, leading to easily repeatable patterns and clean architecture&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/merisbahti" rel="noopener noreferrer"&gt;@merisbahti&lt;/a&gt;'s &lt;a href="https://github.com/merisbahti/jotai-optics" rel="noopener noreferrer"&gt;&lt;code&gt;jotai-optics&lt;/code&gt;&lt;/a&gt; provides a straightforward integration between Jotai and &lt;code&gt;optics-ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Obviously, this was a very simple integration, but we feel it cracks open the door for some powerful functional programming integrations between &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt; and &lt;a href="https://github.com/c0d3t3k/jotai-optics" rel="noopener noreferrer"&gt;jotai-optics&lt;/a&gt; especially in light of the impressive feature set of &lt;a href="https://github.com/akheron/optics-ts" rel="noopener noreferrer"&gt;optics-ts&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/jotaiflix-optics-9qch0" rel="noopener noreferrer"&gt;Codesandbox example&lt;/a&gt; is included below. &lt;/p&gt;

&lt;p&gt;NOTE: This sample code includes &lt;a href="https://github.com/c0d3t3k/jotai-devtools" rel="noopener noreferrer"&gt;Jotai Dev Tools&lt;/a&gt; so be sure to use a &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd" rel="noopener noreferrer"&gt;Redux DevTools Browser Extension&lt;/a&gt; to easily observe the relevant state changes. For more info, please see our &lt;a href="https://dev.to/c0d3t3k/jotai-not-exactly-redux-dev-tools-57i6"&gt;previous article&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/jotaiflix-optics-9qch0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>react</category>
      <category>lenses</category>
      <category>jotai</category>
      <category>functional</category>
    </item>
    <item>
      <title>Jotai (not exactly Redux) Dev Tools</title>
      <dc:creator>c0d3t3k</dc:creator>
      <pubDate>Wed, 28 Oct 2020 22:22:42 +0000</pubDate>
      <link>https://dev.to/c0d3t3k/jotai-not-exactly-redux-dev-tools-57i6</link>
      <guid>https://dev.to/c0d3t3k/jotai-not-exactly-redux-dev-tools-57i6</guid>
      <description>&lt;p&gt;Recently, we have begun investigating &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;, a state management alternative to &lt;a href="https://recoiljs.org/" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; by &lt;a href="https://github.com/pmndrs" rel="noopener noreferrer"&gt;Poimandres&lt;/a&gt;. In addition, we documented some light investigation converting an existing Recoil app to Jotai in a &lt;a href="https://dev.to/c0d3t3k/recoil-vs-jotai-using-typescript-4678"&gt;previous article&lt;/a&gt;. While performing this exercise it got us thinking about what debug tools that might be available which are specifically targeted to Jotai. Unfortunately, as of this writing all that is available is the &lt;a href="https://github.com/pmndrs/jotai/blob/master/docs/debugging.md" rel="noopener noreferrer"&gt;useDebugValue&lt;/a&gt; hook.&lt;/p&gt;

&lt;p&gt;Now, like most folks, we enjoy using powerful and versatile dev tools such as &lt;a href="https://github.com/facebook/react/tree/master/packages/react-devtools" rel="noopener noreferrer"&gt;React DevTools&lt;/a&gt;, &lt;a href="https://github.com/tannerlinsley/react-query-devtools" rel="noopener noreferrer"&gt;React Query DevTools&lt;/a&gt;, etc. While we have not used &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; in a production capacity in the past, (for a number of reasons) we have always preferred to use &lt;a href="https://github.com/reduxjs/redux-devtools" rel="noopener noreferrer"&gt;ReduxDevTools&lt;/a&gt; for debugging React state management systems. In fact, we have created these custom internal plugins for each state management system we have used in our contracting work. As of this writing, we couldn't find a Jotai plugin yet, so naturally we thought it might be interersting to attempt to create one.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv65qonbuv1fgpke4lxlq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv65qonbuv1fgpke4lxlq.gif" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we setup some interfaces/types for Config, Message, ConnectionResult, Extension, etc. to match the payloads transmitted from/to ReduxDevTools. These interfaces enable strongly typed mapping for communication with the &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd" rel="noopener noreferrer"&gt;Browser Extension&lt;/a&gt; proper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;instanceID&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;actionCreators&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;latency&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;predicate&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;autoPause&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;IConnectionResult&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nl"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nl"&gt;send&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ConnectionResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;IConnectionResult&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Extension&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ConnectionResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, the Redux DevTools connect function used for the browser extension frontend does not return a standard &lt;a href="https://github.com/ReactiveX/rxjs" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt; Observable. Consequently, we need the &lt;code&gt;wrapConnectionResult&lt;/code&gt; function to make a RxJS compatible observable to receive ReduxDevTools events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapConnectionResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConnectionResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Subject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we'll implement a &lt;code&gt;JotaiDevtoolsProps&lt;/code&gt; interface so that the user can name and configure DevTools instances.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;JotaiDevtoolsProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WritableAtom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To enable seamless integration with React Functional Components, we create a hook that will take our JotaiDevtoolsProps and instantiate an instance for a particular Jotai atom.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useJotaiDevtools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;JotaiDevtoolsProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we'll use the &lt;a href="https://observable-hooks.js.org/" rel="noopener noreferrer"&gt;&lt;code&gt;observable-hooks&lt;/code&gt;&lt;/a&gt; library's &lt;a href="https://observable-hooks.js.org/api/#useobservable" rel="noopener noreferrer"&gt;&lt;code&gt;useObservable&lt;/code&gt; hook&lt;/a&gt; to provide the value of the incoming atom as an RxJS observable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;atom$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useObservable&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;input$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;input$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;atomCurrentValue&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to prevent a race condition when an atom is updated, we'll add a flag to determine whether the last state update was a ReduxDevTools &lt;em&gt;Time Travel Event&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;wasTriggeredByDevtools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWasTriggeredByDevtools&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, we need a flag to determine if the initial state has already been sent to the DevTools Extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sentInitialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSentInitialState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDevTools&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ConnectionResult&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the DevTools connection may not be availiable when the hook is initialized, we will add another &lt;code&gt;useObservable&lt;/code&gt; to provide a sanitized stream of events when the connection is ready.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;devTools$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useObservable&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;input$&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;input$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;switchMap&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;wrapConnectionResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ConnectionResult&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;observable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is called to handle State Jumps and Time Travel events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jumpToState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWasTriggeredByDevtools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// var oldState = atomCurrentValue();&lt;/span&gt;
        &lt;span class="nf"&gt;setAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// setWasTriggeredByDevtools(false);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Continuing our previous pattern, we will use &lt;code&gt;observable-hook&lt;/code&gt;'s &lt;a href="https://observable-hooks.js.org/api/#usesubscription" rel="noopener noreferrer"&gt;useSubscription&lt;/a&gt; hook to subscribe to the DevTools Extension events and respond appropriately to either either a START or DISPATCH action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;devTools$&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;START&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Atom Devtools Start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;atomCurrentValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sentInitialState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// devTools.send("\"" + options.name + "\" - Initial State", atom.getState());&lt;/span&gt;
                    &lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; - Initial State&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;atomCurrentValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nf"&gt;setSentInitialState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DISPATCH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JUMP_TO_ACTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;JUMP_TO_STATE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="nf"&gt;jumpToState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will need another &lt;code&gt;useSubscribe&lt;/code&gt; hook to subscribe updates coming from application state changes to the current Jotai atom.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;atom$&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasTriggeredByDevtools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;setWasTriggeredByDevtools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; - &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;moment&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the component is initialized, we will need a function to get a reference to the &lt;a href="https://github.com/reduxjs/redux-devtools" rel="noopener noreferrer"&gt;DevTools Extension&lt;/a&gt;. If the extension is not installed, an error will be logged to the console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initDevtools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;devtools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;__REDUX_DEVTOOLS_EXTENSION__&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Extension&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// const options = config;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;devtools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jotai Devtools plugin: Cannot find Redux Devtools browser extension. Is it installed?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;devTools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;devtools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Dev Tools&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="nf"&gt;setDevTools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;devTools&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// setTimeout(() =&amp;gt; devTools.send(name + " - Initial State", atomCurrentValue), 50)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to activate our initialization function, we will utilize the &lt;code&gt;useLifeCycles&lt;/code&gt; hook (from the excellent &lt;a href="https://github.com/streamich/react-use/blob/master/docs/useLifecycles.md" rel="noopener noreferrer"&gt;react-use&lt;/a&gt; library) to handle the component mount lifecycle event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;    &lt;span class="nf"&gt;useLifecycles&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;initDevtools&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Now just install the new &lt;a href="https://github.com/c0d3t3k/jotai-devtools" rel="noopener noreferrer"&gt;Jotai Devtools plugin&lt;/a&gt; in any projects that utilize &lt;a href="https://github.com/pmndrs/jotai#usetheatom" rel="noopener noreferrer"&gt;Jotai atoms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Specifically, we can just call the &lt;code&gt;useJotaiDevtools&lt;/code&gt; hook for each atom you want to view in the &lt;a href="https://github.com/reduxjs/redux-devtools" rel="noopener noreferrer"&gt;Devtool Browser Extension&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="nf"&gt;useJotaiDevtools&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dark Mode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;darkModeState&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

    &lt;span class="nf"&gt;useJotaiDevtools&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tasksAtom&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For illustration, we can re-use the &lt;a href="https://recoiljs.org/" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; example we converted to &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt; in a &lt;a href="https://dev.to/c0d3t3k/recoil-vs-jotai-using-typescript-4678"&gt;previous post&lt;/a&gt;. Once the app is started, we can open the &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd" rel="noopener noreferrer"&gt;Redux DevTools Browser Extension&lt;/a&gt; and we will be able to watch our state changes, time travel debug, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv65qonbuv1fgpke4lxlq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fv65qonbuv1fgpke4lxlq.gif" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;By leveraging the existing work done on ReduxDevTools, we have access to a useful debugging aid without reinventing the wheel.&lt;/li&gt;
&lt;li&gt;Leveraging the &lt;code&gt;observable-hooks&lt;/code&gt; and &lt;code&gt;react-use&lt;/code&gt; libraries enabled clean and efficient ReduxDevTools integration.&lt;/li&gt;
&lt;li&gt;Adapting the time travel functionality of ReduxDevTools enables full replay of the Jotai chain of state.&lt;/li&gt;
&lt;li&gt;The result is a compelling insight into the Jotai atom lifecycle.&lt;/li&gt;
&lt;li&gt;Check out the &lt;a href="https://github.com/c0d3t3k/jotai-devtools" rel="noopener noreferrer"&gt;Jotai Devtools Repo on Github&lt;/a&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below is a functioning example. If you haven't already, make sure you install the &lt;a href="https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd" rel="noopener noreferrer"&gt;Redux DevTools extension&lt;/a&gt; to be able to see the state update.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/happy-shockley-35roh"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>reduxdevtools</category>
      <category>debugging</category>
      <category>jotai</category>
      <category>timetravel</category>
    </item>
    <item>
      <title>Recoil to Jotai (with Typescript)</title>
      <dc:creator>c0d3t3k</dc:creator>
      <pubDate>Wed, 28 Oct 2020 16:42:17 +0000</pubDate>
      <link>https://dev.to/c0d3t3k/recoil-vs-jotai-using-typescript-4678</link>
      <guid>https://dev.to/c0d3t3k/recoil-vs-jotai-using-typescript-4678</guid>
      <description>&lt;p&gt;Our consulting team has enjoyed using several excellent &lt;a href="https://github.com/pmndrs" rel="noopener noreferrer"&gt;react libraries&lt;/a&gt; such as &lt;a href="https://github.com/pmndrs/react-spring" rel="noopener noreferrer"&gt;react-spring&lt;/a&gt;, &lt;a href="https://github.com/pmndrs/react-three-fiber" rel="noopener noreferrer"&gt;react-three-fiber&lt;/a&gt;, &lt;a href="https://github.com/pmndrs/react-three-flex" rel="noopener noreferrer"&gt;react-three-flex&lt;/a&gt; lately. As a result, we were intrigued when &lt;a href="https://github.com/pmndrs" rel="noopener noreferrer"&gt;Poimandres'&lt;/a&gt; &lt;a href="https://twitter.com/0xca0a/status/1303354125246255104?lang=en" rel="noopener noreferrer"&gt;announced&lt;/a&gt; &lt;a href="https://github.com/pmndrs/jotai" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;, a &lt;a href="https://recoiljs.org/" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; state management alternative. Couple this with the fact we have been using more and more &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;, we thought it might be interesting to explore the differences between a &lt;em&gt;Recoil&lt;/em&gt; project and one implemented in &lt;em&gt;Jotai&lt;/em&gt; with respect to explicit typing. &lt;/p&gt;

&lt;p&gt;In an attempt to approximate an 'apples to apples' comparison, we decided on &lt;a href="https://twitter.com/jacques_codes" rel="noopener noreferrer"&gt;Jaques Bloms'&lt;/a&gt; &lt;a href="https://github.com/jacques-blom/recoil-todo-list/tree/completed" rel="noopener noreferrer"&gt;recoil-todo-list&lt;/a&gt; as a starting point. It not only uses Typescript, but also utilizes a number of &lt;a href="https://recoiljs.org/docs/introduction/core-concepts" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; idioms like &lt;a href="https://recoiljs.org/docs/introduction/core-concepts#atoms" rel="noopener noreferrer"&gt;Atoms&lt;/a&gt;, &lt;a href="https://recoiljs.org/docs/introduction/core-concepts#selectors" rel="noopener noreferrer"&gt;Selectors&lt;/a&gt; and &lt;a href="https://recoiljs.org/docs/api-reference/utils/atomFamily/" rel="noopener noreferrer"&gt;AtomFamily&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below are some highlights of the &lt;a href="https://github.com/c0d3t3k/recoil-todo-list/tree/jotai" rel="noopener noreferrer"&gt;recoil-todo-list&lt;/a&gt; conversion. These steps attempt to illustrate some of the syntatic/algorithmic differences between the two libraries. So let's dive in!&lt;/p&gt;

&lt;p&gt;Similar to Recoil, Jotai uses a &lt;a href="https://reactjs.org/docs/context.html#contextprovider" rel="noopener noreferrer"&gt;context provider&lt;/a&gt; to enable app wide access to state. After &lt;a href="https://www.npmjs.com/package/jotai" rel="noopener noreferrer"&gt;installing Jotai&lt;/a&gt; just needed to modify the &lt;code&gt;index.tsx&lt;/code&gt; from Recoil's &lt;code&gt;&amp;lt;RecoilRoot&amp;gt;&lt;/code&gt; to Jotai's &lt;code&gt;&amp;lt;Provider&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jotai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;//import {RecoilRoot} from 'recoil'&lt;/span&gt;

&lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;RecoilRoot&amp;gt; */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* &amp;lt;/RecoilRoot&amp;gt; */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StrictMode&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The snippet below implements the app's obligatory &lt;a href="https://en.wikipedia.org/wiki/Light-on-dark_color_scheme" rel="noopener noreferrer"&gt;Dark Mode&lt;/a&gt; state management. In &lt;code&gt;Header.tsx&lt;/code&gt; just need a small syntatic change to &lt;a href="https://github.com/pmndrs/jotai#usetheatom" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;'s &lt;code&gt;{atom, useAtom}&lt;/code&gt; from &lt;a href="https://recoiljs.org/docs/introduction/core-concepts#atoms" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt;'s &lt;code&gt;{atom, useRecoilState}&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Header.tsx except&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// RECOIL //&lt;/span&gt;
    &lt;span class="c1"&gt;//const [darkMode, setDarkMode] = useRecoilState(darkModeState)&lt;/span&gt;

    &lt;span class="c1"&gt;// JOTAI //&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;darkMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDarkMode&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;darkModeState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we needed to convert &lt;code&gt;Tasks.tsx&lt;/code&gt;. We chose to go with an Task interface in order to custom defined type a &lt;code&gt;TasksAtom&lt;/code&gt; that will be used to store the Task indexes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Tasks.tsx excerpt&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;// RECOIL //&lt;/span&gt;
&lt;span class="c1"&gt;// export const tasksState = atom&amp;lt;number[]&amp;gt;({&lt;/span&gt;
&lt;span class="c1"&gt;//     key: 'tasks',&lt;/span&gt;
&lt;span class="c1"&gt;//     default: [],&lt;/span&gt;
&lt;span class="c1"&gt;// })&lt;/span&gt;

&lt;span class="c1"&gt;// export const tasksState = atom([] as number[])&lt;/span&gt;

&lt;span class="c1"&gt;// JOTAI //&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasksAtom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;atom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasksAtom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we converted &lt;a href="https://github.com/c0d3t3k/recoil-todo-list/blob/jotai/src/components/Tasks.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;Task.tsx&lt;/code&gt;&lt;/a&gt;, using a Jotai &lt;code&gt;util&lt;/code&gt; implementation similar to Recoil's &lt;a href="https://recoiljs.org/docs/api-reference/utils/atomFamily/" rel="noopener noreferrer"&gt;&lt;code&gt;atomFamily&lt;/code&gt;&lt;/a&gt;. Notice here that Jotai's implementation of &lt;a href="https://github.com/pmndrs/jotai/blob/master/docs/utils.md#atomfamily" rel="noopener noreferrer"&gt;&lt;code&gt;atomFamily&lt;/code&gt;&lt;/a&gt; includes an explicit definition of a getter and setter which internally utilizes the &lt;code&gt;tasksAtom&lt;/code&gt; defined in &lt;a href="https://github.com/c0d3t3k/recoil-todo-list/blob/jotai/src/components/Tasks.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;Tasks.tsx&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Btw, &lt;a href="https://github.com/pmndrs/jotai/pull/45" rel="noopener noreferrer"&gt;Jotai Pull Request #45&lt;/a&gt; went a long way in helping us understand how this should work (props to &lt;a href="https://github.com/dai-shi" rel="noopener noreferrer"&gt;@dai-shi&lt;/a&gt; and &lt;a href="https://github.com/brookslybrand" rel="noopener noreferrer"&gt;@brookslybrand&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Task.tsx excerpt&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// RECOIL //&lt;/span&gt;
&lt;span class="c1"&gt;// export const taskState = atomFamily({&lt;/span&gt;
&lt;span class="c1"&gt;//     key: 'task',&lt;/span&gt;
&lt;span class="c1"&gt;//     default: {&lt;/span&gt;
&lt;span class="c1"&gt;//         label: '',&lt;/span&gt;
&lt;span class="c1"&gt;//         complete: false,&lt;/span&gt;
&lt;span class="c1"&gt;//     },&lt;/span&gt;
&lt;span class="c1"&gt;// })&lt;/span&gt;

&lt;span class="c1"&gt;// JOTAI //&lt;/span&gt;
&lt;span class="c1"&gt;// https://github.com/pmndrs/jotai/pull/45&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;taskState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atomFamily&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ITask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//const [{complete, label}, setTask] = useRecoilState(taskState(id))&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;setTask&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAtom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;taskState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next file to convert is &lt;a href="https://github.com/c0d3t3k/recoil-todo-list/blob/jotai/src/components/Input.tsx" rel="noopener noreferrer"&gt;&lt;code&gt;Input.tsx&lt;/code&gt;&lt;/a&gt;. We chose to substitute the Recoil &lt;a href="https://recoiljs.org/docs/api-reference/core/useRecoilCallback" rel="noopener noreferrer"&gt;useRecoilCallback&lt;/a&gt; with Jotai's &lt;a href=""&gt;useAtomCallback&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Input.tsx excerpt&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;// RECOIL&lt;/span&gt;
    &lt;span class="c1"&gt;// const insertTask = useRecoilCallback(({set}) =&amp;gt; {&lt;/span&gt;
        &lt;span class="c1"&gt;//     return (label: string) =&amp;gt; {&lt;/span&gt;
        &lt;span class="c1"&gt;//         const newTaskId = tasks.length&lt;/span&gt;
        &lt;span class="c1"&gt;//         set(tasksState, [...tasks, newTaskId])&lt;/span&gt;
        &lt;span class="c1"&gt;//         set(taskState(newTaskId), {&lt;/span&gt;
        &lt;span class="c1"&gt;//             label: label,&lt;/span&gt;
        &lt;span class="c1"&gt;//             complete: false,&lt;/span&gt;
        &lt;span class="c1"&gt;//         })&lt;/span&gt;
        &lt;span class="c1"&gt;//     }&lt;/span&gt;
        &lt;span class="c1"&gt;// })&lt;/span&gt;

    &lt;span class="c1"&gt;// JOTAI //&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;insertTask&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAtomCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
        &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newTaskId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
        &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasksAtom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTaskId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;taskState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newTaskId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, in &lt;code&gt;Stats.tsx&lt;/code&gt;, we replaced the &lt;a href="https://recoiljs.org/docs/api-reference/core/selector/" rel="noopener noreferrer"&gt;Recoil Selectors&lt;/a&gt; with readonly Jotai Atoms using computed Task state. In this case, there appears to be only a slight syntatic difference, mostly around the use of string reference keys.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Stats.tsx excerpt&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// RECOIL //&lt;/span&gt;
&lt;span class="cm"&gt;/*
const tasksCompleteState = selector({
    key: 'tasksComplete',
    get: ({get}) =&amp;gt; {
        const taskIds = get(tasksState)
        const tasks = taskIds.map((id) =&amp;gt; {
            return get(taskState(id))
        })
        return tasks.filter((task) =&amp;gt; task.complete).length
    },
})

const tasksRemainingState = selector({
    key: 'tasksRemaining',
    get: ({get}) =&amp;gt; {
        const taskIds = get(tasksState)
        const tasks = taskIds.map((id) =&amp;gt; {
            return get(taskState(id))
        })
        return tasks.filter((task) =&amp;gt; !task.complete).length
    },
})
*/&lt;/span&gt;

&lt;span class="c1"&gt;// JOTAI&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasksCompleteState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasksState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasksAtom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tasksState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;taskState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasksRemainingState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasksState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasksAtom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tasksState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;taskState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Thoughts:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Overall, we were impressed by how things "just worked".&lt;/li&gt;
&lt;li&gt;The syntactic differences were easy to navigate as well as the different mechanisms for referencing atoms.&lt;/li&gt;
&lt;li&gt;With the relative lack of documentation currently available, we recommend reviewing &lt;a href="https://github.com/pmndrs/jotai/issues?q=" rel="noopener noreferrer"&gt;Jotai issues and pull requests&lt;/a&gt; to get more familiar with the concepts and techniques.&lt;/li&gt;
&lt;li&gt;We enjoyed this exercise and as a result will be doing more investigation into using Jotai in our production solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/c0d3t3k/recoil-todo-list/tree/jotai" rel="noopener noreferrer"&gt;Github Source&lt;/a&gt; and &lt;a href="https://codesandbox.io/s/recoil-to-jotai-s9jm5" rel="noopener noreferrer"&gt;CodeSandbox&lt;/a&gt; are also available.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/recoil-to-jotai-s9jm5"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>recoil</category>
      <category>jotai</category>
      <category>typescript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
