<?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: Hồng Phát</title>
    <description>The latest articles on DEV Community by Hồng Phát (@nguyenhongphat0).</description>
    <link>https://dev.to/nguyenhongphat0</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%2F1059168%2Feaf079de-143a-401f-86cb-4b7f00bac594.png</url>
      <title>DEV Community: Hồng Phát</title>
      <link>https://dev.to/nguyenhongphat0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nguyenhongphat0"/>
    <language>en</language>
    <item>
      <title>Redux developers, please stop doing this!</title>
      <dc:creator>Hồng Phát</dc:creator>
      <pubDate>Sat, 17 Aug 2024 16:36:23 +0000</pubDate>
      <link>https://dev.to/nguyenhongphat0/redux-developers-please-stop-doing-this-107a</link>
      <guid>https://dev.to/nguyenhongphat0/redux-developers-please-stop-doing-this-107a</guid>
      <description>&lt;p&gt;I worked on a React project in 2019, I believe it was built on top of the &lt;a href="https://github.com/react-boilerplate/react-boilerplate" rel="noopener noreferrer"&gt;react-boilerplate&lt;/a&gt; template, and the developer experience with Redux was so bad that I became a Vue developer.&lt;/p&gt;

&lt;p&gt;After a few years working with Vuex, Pinia, Recoil (yes, I came back), and recently Jotai (which is my go-to state management library at the moment), a friend randomly asked for my help in debugging the flow of his app, and it amazed me how Redux developers still write that boilerplate code:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cartSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&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;cart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// x, y, z,…&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setTotal&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="nx"&gt;action&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nf"&gt;pushItem&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="nx"&gt;action&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="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;=&lt;/span&gt; &lt;span class="nx"&gt;action&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&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="nf"&gt;clearItems&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&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="c1"&gt;// setX, setY, setZ…&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;This was exactly the reason why I had such a bad time maintaining that Redux project. And it was even worse without Redux Toolkit (which they only introduced recently):&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SET_TOTAL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SET_TOTAL&lt;/span&gt;&lt;span class="dl"&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;setTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&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;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SET_TOTAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;total&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;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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="nx"&gt;SET_TOTAL&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;total&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="nx"&gt;payload&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="nl"&gt;default&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;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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTotal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&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;total&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;mapStateToProps&lt;/span&gt; &lt;span class="o"&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="na"&gt;total&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getTotal&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;setTotal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&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;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;setTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;total&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="k"&gt;default&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;mapStateToProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mapDispatchToProps&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&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;{&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;total&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&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="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;You&lt;/span&gt; &lt;span class="nx"&gt;must&lt;/span&gt; &lt;span class="nx"&gt;pay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;total&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;/b&amp;gt;&lt;/span&gt;&lt;span class="err"&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;This was not a joke! In fact, cumbersome, excessive boilerplate code like the example above was often considered &lt;em&gt;"best practices"&lt;/em&gt; and &lt;em&gt;"industry standards"&lt;/em&gt; in my observation. It was almost as if we were writing code like this:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isDifferent&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="nx"&gt;y&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;x&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;y&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;isGreater&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="nx"&gt;y&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;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;y&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;subtract&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="nx"&gt;y&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;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;y&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;greatestCommonDivisor&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="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isDifferent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isGreater&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="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subtract&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="nx"&gt;y&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subtract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&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="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above code is far from being &lt;em&gt;"highly scalable"&lt;/em&gt;, providing &lt;em&gt;"the best developer experience"&lt;/em&gt;, or focusing on &lt;em&gt;"performance and best practices"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Don’t get me wrong! Redux’s pattern itself is not bad; in fact, it’s so good that React introduced the &lt;a href="https://react.dev/reference/react/useReducer" rel="noopener noreferrer"&gt;useReducer&lt;/a&gt; hook to mimic it. The issue is that a marginal number of React developers are doing it wrong. Here’s how to do it right:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batch relevant updates&lt;/strong&gt;: Instead of creating &lt;strong&gt;data-oriented&lt;/strong&gt; actions like &lt;code&gt;PUSH_ITEM&lt;/code&gt; and &lt;code&gt;SET_TOTAL&lt;/code&gt;, focus on &lt;strong&gt;behavior-oriented&lt;/strong&gt; actions like &lt;code&gt;ADD_TO_CART&lt;/code&gt;, which handle multiple updates (adding an item and updating the total) in one go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep the state simple and move complex logic to selectors&lt;/strong&gt;: For example, you can remove computable fields like &lt;code&gt;total&lt;/code&gt; from your state entirely, then calculate them automatically from cart &lt;code&gt;items&lt;/code&gt; using &lt;code&gt;reselect&lt;/code&gt;. This approach not only ensures data integrity but also makes the code easier to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please share this post with anyone you know who writes boilerplate-heavy Redux code, so we can all make state management in single-page applications less of a pain!&lt;/p&gt;

</description>
      <category>react</category>
      <category>redux</category>
      <category>boilerplate</category>
      <category>code</category>
    </item>
    <item>
      <title>I used react-router but not for routing</title>
      <dc:creator>Hồng Phát</dc:creator>
      <pubDate>Fri, 21 Jun 2024 16:31:57 +0000</pubDate>
      <link>https://dev.to/nguyenhongphat0/i-used-react-router-but-not-for-routing-19k3</link>
      <guid>https://dev.to/nguyenhongphat0/i-used-react-router-but-not-for-routing-19k3</guid>
      <description>&lt;p&gt;I was working on my React project, fetching some data using &lt;code&gt;fetch&lt;/code&gt;, and looking for a good way to do something like this Python code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{a} {b}!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;World&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# == "Hello World!"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Why not use JavaScript string interpolation, you may ask? I want to put the params elsewhere, not within the string.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I &lt;del&gt;spent hours doing Regex&lt;/del&gt; went code scavenging on Stack Overflow, and I found &lt;a href="https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format" rel="noopener noreferrer"&gt;this&lt;/a&gt;. It’s interesting that there is a function named &lt;a href="https://stackoverflow.com/a/18234317" rel="noopener noreferrer"&gt;&lt;code&gt;formatUnicorn&lt;/code&gt;&lt;/a&gt; right on Stack Overflow’s website, which does exactly what I was looking for:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh38wfy9jqfkk7txm6ywg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh38wfy9jqfkk7txm6ywg.png" alt="Stack Overflow's formatUnicorn" width="800" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But I was hesitant to loot the code. I want to keep my code base as strongly typed as possible, and modifying the String prototype makes me feel insecure. What’s more, I would like to have at least some validation in case someone else forgot to pass in &lt;code&gt;a&lt;/code&gt; and/or &lt;code&gt;b&lt;/code&gt;. Implementing that seems beyond my scope of work, and installing another library is overkill. I was staring at the import section (just to make sure that I don’t import so much 3rd party stuff to keep our SPA app startup fast since FCP is crucial to our business), and that’s when I saw &lt;code&gt;react-router&lt;/code&gt; import…&lt;/p&gt;

&lt;p&gt;React Router lets you register routes with params like this:&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="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;teams/:teamId&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;So they must have implemented that “unicorn format” somewhere. I skimmed over their exports hoping that they would expose that unicorn function so I could use it, and thankfully they did:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ar1103vfjnsr5q79px7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ar1103vfjnsr5q79px7.png" alt="import { generatePath } from " width="800" height="233"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the function is unicornly strong typed:&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpc2ue511kqf3dk25l8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftpc2ue511kqf3dk25l8q.png" alt="IDE code completion and type checking for params" width="800" height="183"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Did you expect that TypeScript can infer substrings as types like above? I didn’t!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The moral of the story
&lt;/h3&gt;

&lt;p&gt;Sometimes, installing one more library may not be necessary because what you need is already (unexpectedly) available elsewhere within your project. Bundling too many libraries can greatly affect performance if it is crucial to your business.&lt;/p&gt;

&lt;p&gt;I hope my story was inspiring!&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>storytelling</category>
      <category>performance</category>
    </item>
    <item>
      <title>Generate API Clients: A new way to consume REST APIs and GraphQL</title>
      <dc:creator>Hồng Phát</dc:creator>
      <pubDate>Tue, 30 Apr 2024 15:21:01 +0000</pubDate>
      <link>https://dev.to/nguyenhongphat0/a-new-way-to-consume-rest-apis-and-graphql-3l8k</link>
      <guid>https://dev.to/nguyenhongphat0/a-new-way-to-consume-rest-apis-and-graphql-3l8k</guid>
      <description>&lt;p&gt;Integrating HTTP APIs often involves tedious double-checking:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Was the endpoint &lt;code&gt;GET /sheep&lt;/code&gt; or &lt;code&gt;GET /sheeps&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Should the parameters be put in the URL query string, request body, or even headers?&lt;/li&gt;
&lt;li&gt;Is the API server expecting to receive parameters in the Content-Type &lt;code&gt;application/json&lt;/code&gt; or &lt;code&gt;x-www-form-url-encoded&lt;/code&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By generating HTTP API clients from existing API endpoints using the approaches below, we can make the development more enjoyable while improving the UX by reducing bundle size and startup time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why generate API clients?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Absolute accuracy:&lt;/strong&gt; Generated clients follow specifications such as GraphQL SDL or Swagger OpenAPI to ensure your server receives what it demands. Things can’t go wrong unless you choose to disregard TypeScript declarations by unleashing the power of the &lt;code&gt;any&lt;/code&gt; keyword.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term productivity:&lt;/strong&gt; It takes only a few minutes to generate your API client, which then saves you hours and hours writing functions for each endpoint, manually defining their request params and response payloads. What's more, it saves you the effort of fixing any potential errors by doing so manually. I've been there!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outstanding IDE support:&lt;/strong&gt; Witness yourself in the GraphQL section below!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to generate API Clients?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  RESTful APIs via &lt;a href="https://github.com/acacode/swagger-typescript-api" rel="noopener noreferrer"&gt;swagger-typescript-api&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As the library name suggests, using this approach requires your  RESTful APIs documented using the OpenAPI Specification (formerly Swagger). Server-side frameworks like Java Spring can often generate this documentation for you effortlessly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you use other specifications, there might be an equivalent library available.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install &lt;code&gt;swagger-typescript-api&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; swagger-typescript-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate your client:&lt;/strong&gt;&lt;br&gt;
You can either execute the command directly or add a script entry to your &lt;code&gt;package.json&lt;/code&gt; file for easy regeneration when your backend APIs change:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gen:rest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"swagger-typescript-api -p https://your-server.com/rest-api-endpoint/docs -o ./src/services -n rest.ts"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Make sure the &lt;code&gt;-p&lt;/code&gt; parameter points to your Swagger schema, usually a JSON/YML endpoint (not the Web UI endpoint). Run &lt;code&gt;npm run gen:rest&lt;/code&gt; to generate your client into &lt;code&gt;src/services/rest.ts&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use generated client in your code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Api&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;services/rest&lt;/span&gt;&lt;span class="dl"&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Api&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;securityWorker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use this to set up Bearer tokens in request headers&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;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;page&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="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// TypeScript will let you know which params can be passed in&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The best part is that your response data is always strongly typed. This means that whenever you type &lt;code&gt;data.&lt;/code&gt; and press &lt;code&gt;Ctrl&lt;/code&gt;+&lt;code&gt;Space&lt;/code&gt; or &lt;code&gt;⌥&lt;/code&gt; + &lt;code&gt;Esc&lt;/code&gt;, your IDE will let you know precisely what lives inside your data, fields that you didn't even know existed and fields that you think you could use but, in fact, could not. No verbose code is required!&lt;/p&gt;

&lt;h2&gt;
  
  
  GraphQL via &lt;a href="https://github.com/remorses/genql" rel="noopener noreferrer"&gt;genql&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I actually discovered this mechanism and decided to write a blog about it while working with GraphQL. As someone who embraces the KISS principle, I found working with Apollo clients to be quite daunting: writing GraphQL queries within large JavaScript strings with limited extension support, intricate approach to customizing outgoing requests (such as placing tokens in headers and retrying after token refreshes), or caching behaviour that often differed from expectations. &lt;/p&gt;

&lt;p&gt;On the other hand, generating clients for GraphQL is much simpler because all endpoints already adhere to a single specification. Honestly, I believe &lt;code&gt;genql&lt;/code&gt; should be the official solution for web GraphQL clients.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install &lt;code&gt;@genql/cli&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; @genql/cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Generate your client:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"gen:gql"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"genql --endpoint https://your-server.com/graphql/ --output ./src/services/graphql"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then, you can simply run &lt;code&gt;npm run gen:gql&lt;/code&gt; to generate your client.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use generated client in your code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClient&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;services/graphql&lt;/span&gt;&lt;span class="dl"&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Customize the fetch function to add JWT tokens or implement retry logic&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// IDEs with TypeScript support will assist you with typing all of the code below effortlessly&lt;/span&gt;
  &lt;span class="na"&gt;products&lt;/span&gt;&lt;span class="p"&gt;:&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Pick only the fields you want&lt;/span&gt;
    &lt;span class="na"&gt;__scalar&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;// Or pick all the fields with primitive values&lt;/span&gt;
    &lt;span class="na"&gt;reviews&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;rating&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;// Nested picking allowed&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;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What is truly impressive: you get a subtype with only the fields you requested. Your IDE will discourage you from typing &lt;code&gt;.content&lt;/code&gt; after &lt;code&gt;data.products[0].reviews[0]&lt;/code&gt; because it wasn't queried, even though it exists in the user schema. This would require much more verbose code in Apollo clients. Kudos to the &lt;code&gt;genql&lt;/code&gt; and TypeScript developers!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Generated TypeScript API clients offer a powerful and efficient way to interact with HTTP APIs, whether you are working with GraphQL or REST. If you're looking to improve your API consumption workflow, I highly recommend giving them a try!&lt;/p&gt;

</description>
      <category>api</category>
      <category>client</category>
      <category>typescript</category>
      <category>graphql</category>
    </item>
    <item>
      <title>React State Management in 2024</title>
      <dc:creator>Hồng Phát</dc:creator>
      <pubDate>Fri, 08 Dec 2023 06:34:06 +0000</pubDate>
      <link>https://dev.to/nguyenhongphat0/react-state-management-in-2024-5e7l</link>
      <guid>https://dev.to/nguyenhongphat0/react-state-management-in-2024-5e7l</guid>
      <description>&lt;p&gt;In my POV, React state management libraries can be divided into three groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Reducer-based&lt;/strong&gt;: requires dispatching actions to update a big centralised state, often called a “single source of truth”. In this group, we have &lt;a href="https://redux.js.org" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; and &lt;a href="https://zustand-demo.pmnd.rs" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atom-based&lt;/strong&gt;: splits states into tiny pieces of data called atoms, which can be written to and read from using React hooks. In this group, we have &lt;a href="https://recoiljs.org" rel="noopener noreferrer"&gt;Recoil&lt;/a&gt; and &lt;a href="https://jotai.org" rel="noopener noreferrer"&gt;Jotai&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutable-based&lt;/strong&gt;: leverages proxy to create mutable data sources which can be directly written to or reactively read from. Candidates in this group are &lt;a href="https://mobx.js.org" rel="noopener noreferrer"&gt;MobX&lt;/a&gt; and &lt;a href="https://valtio.pmnd.rs" rel="noopener noreferrer"&gt;Valtio&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we've covered the three main categories of React state management libraries. Let's delve deeper into each one and explore the strengths and weaknesses of each approach. This will help you understand which library best suits your project's needs:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Reducer-based Libraries:
&lt;/h2&gt;

&lt;p&gt;Despite its common criticism about being (overly) complicated, Redux has been the most popular state management library since its creation.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

+---------------------+
|        Actions      |
+----------|----------+
           |
           v
+---------------------+        +---------------------+
|        Reducers     |        |       Store         |
+----------|----------+        +----------|----------+
           |                              |
           v                              v
+---------------------+        +---------------------+
|        State        |        |     Subscriptions   |
+---------------------+        +---------------------+


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A powerful state machine and time machine. Suppose all of your application states live inside the centralised state (which rarely happens because you might have local states in your components), this formula will exist: &lt;code&gt;UI = React(state)&lt;/code&gt;. This means a single state value will only result in one UI, so your application will look consistently the same with a specific state. If you backup the entire state somewhere, then dispatch a change like &lt;code&gt;REVERT(pastState) { state = pastState }&lt;/code&gt;, your UI will be restored as if it was a captured screenshot.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The best DevTools support: By updating the state using explicit actions, DevTools can help you point out what, when and how the state changes. You can imagine it like having a Git commit history in your application state, how cool is it?&lt;br&gt;
&lt;a href="https://media.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%2Fo7kisc3i0as2eav4oogt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fo7kisc3i0as2eav4oogt.png" alt="Commit messages meme. Source: https://medium.com/@hritik.jaiswal/how-to-write-a-good-commit-message-9d2d533b9052"&gt;&lt;/a&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Boilerplate code: even a simple change to your state requires considerable changes in the code.&lt;/li&gt;
&lt;li&gt;Steep learning curve: while it is simple at its core, it is never enough on its own. To truly master Redux, you should know how to use it with other libraries such as Saga, Thunk, Reselect, Immer, or Redux Toolkit. It feels overkill when most of the time, we use generators in Saga just to fetch some data over the network. Modern JS developers tend to use async/await on a day-by-day basis.&lt;/li&gt;
&lt;li&gt;TypeScript: although fully support TypeScript, explicit typing is required most of the time to get typing done for actions, reducers, selectors, and state. Other approaches directly support automatic type inference.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Atom-based Libraries:
&lt;/h2&gt;

&lt;p&gt;Instead of putting your whole application state inside a large centralised state, this approach splits it into multiple atoms, each atom preferably as tiny as primitive types or basic data structures like arrays and flat objects. Then, you can use the selector to group related states together later if you need to.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

+---------------------+
|     Atoms (State)   |
+----------|----------+
           |
           v
+---------------------+        +---------------------+
|  Selectors (Derived |        |   RecoilRoot        |
|     State)          |        +----------|----------+
+----------|----------+                   |
           v                              v
+---------------------+        +---------------------+
|    State Snapshot   |        |   React Components  |
+---------------------+        +---------------------+


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Leverage React features: this is expected since Recoil and React are both created by Facebook. Recoil works great with cutting-edge React features such as Suspense, Transition API and Hooks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Simple and scalable: by using only atoms and selectors, you can still effectively build up a giant reactive application state while having fine-grained control over individual state changes. Lifting state up is now as simple as declaring an atom and changing your &lt;code&gt;useState&lt;/code&gt; hook to &lt;code&gt;useRecoilState&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;TypeScript: as a developer who cares about DX as much as a user cares about UI and UX, I found React, Recoil, and TypeScript to be a wonderful combination. In my projects, types are automatically inferred most of the time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;DevTools: if you are looking for an equivalent of Redux DevTools, unfortunately, there isn’t.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cannot use state outside of components: although Recoil Nexus is a workaround, this kind of state management library is designed with a (maybe true) assumption that all usage of state happens inside React components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not stable (yet): it has been 4 years, and the latest version of Recoil still has the leading 0 (v0.7.7). I would be glad if, by the time you read this, this information stays irrelevant.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Mutable-based Libraries:
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Tips: "mutable" and "immutable" refer to how data can be changed after it is created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;person.age += 1 // mutable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;person = { …person, age: person.age + 1 } // immutable&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

+---------------------+
|     Observables     |
+----------|----------+
           |
           v
+---------------------+        +---------------------+
|   Computed Values   |        |     Actions         |
+----------|----------+        +----------|----------+
           |                              |
           v                              v
+---------------------+        +---------------------+
|   Reaction (Derived |        |    MobX Store       |
|       Value)        |        +----------|----------+
+---------------------+                   |
                                          v
                               +---------------------+
                               |   React Components  |
                               +---------------------+


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

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The simplest API: by allowing the state to be mutated directly, no boilerplate code is required to sit between your component and state, unless you want to do so.&lt;/li&gt;
&lt;li&gt;Reactivity and flexibility: dependencies are updated automatically whenever the state changes. This simplifies your application logic and makes it easier to comprehend. Moreover, the proxy-based approach helps minimise unnecessary re-renders. This also translates to smooth performance and a more responsive user experience.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Too much magic: automatic reactivity is a double-edged sword. Race conditions in asynchronous updates can lead your application state to chaos, and debugging the flow of changes can be challenging in complex applications.&lt;/li&gt;
&lt;li&gt;DevTools: again, it seems to me that no alternative has the best tooling support as the reducer-based approach.&lt;/li&gt;
&lt;li&gt;Discrete DX: while React elaborates on the “immutable” approach, having “mutable” data mixed in my project sometimes makes me feel insecure about how I should make changes to my data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The best choice
&lt;/h1&gt;

&lt;p&gt;Again, the best React state management library for your project depends on your and your team’s specific needs and expertise. Please &lt;strong&gt;DON'T&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Pick a library based solely on project size and complexity. Because, you may have heard somewhere that X is more suitable for a large-scale project while Y is better for a smaller one. Library authors designed their libraries with scalability in mind, and your project’s scalability depends on how you write the code and use the library, not which libraries you choose to work with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Apply best practices you learned from one library to another. Putting your whole application state inside a single Recoil atom to achieve a “single source of truth” will only lead to struggling with state updates and issues with performance. As well as defining actions in Redux as if they were setters and dispatching multiple of them instead of batching changes in one commit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  The author's choice
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR: Jotai.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I personally prefer the atomic libraries because of the advantages listed above and my historical painless DX when dealing with asynchronous data fetching and batching loading UI with &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt;. What Jotai does better than Recoil is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No key is required. Naming things is tough, and most of the time, you won’t use Recoil’s keys. So why spend time declaring them at all when the libraries can automatically have the keys for you? Here is &lt;a href="https://github.com/facebookexperimental/Recoil/issues/378" rel="noopener noreferrer"&gt;Recoil’s answer&lt;/a&gt;; however, as you can see, people are not quite convinced.&lt;/li&gt;
&lt;li&gt;Performance. A picture is worth a thousand words, and I have 4 of them: &lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Import cost&lt;/th&gt;
&lt;th&gt;LCP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Recoil&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w4upwu39rucvne3a63k4.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.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%2Fw4upwu39rucvne3a63k4.png" alt="Recoil import cost"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snijw0t3v6acd204g8h1.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.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%2Fsnijw0t3v6acd204g8h1.png" alt="Recoil usage's LCP"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jotai&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/atprlx3589csb220r23c.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.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%2Fatprlx3589csb220r23c.png" alt="Jotai import cost"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h61hxs2a9cb33bx2951w.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.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%2Fh61hxs2a9cb33bx2951w.png" alt="Jotai usage's LCP"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You might argue that a ~20Kb difference in size does not matter that much, but let’s take a look at a benchmark which was taken on a very old Android device, where sluggishness appears as obvious as bars filled in with a pattern of diagonal red stripes. As you can see, Jotai internal logic requires less overall calculation, which improved my application's LCP, an important Core Web Vitals metric, from ~2.6s to ~1.2s. Nonetheless, this comparison may not take into account other factors that Recoil do better than Jotai (in fact, my knowledge cutoff in this). I just want to say that the Jotai team did a wonderful job there.&lt;/p&gt;

&lt;p&gt;I hope this helps!&lt;/p&gt;

</description>
      <category>react</category>
      <category>state</category>
      <category>redux</category>
      <category>comparison</category>
    </item>
  </channel>
</rss>
