<?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: Lior Bragilevsky</title>
    <description>The latest articles on DEV Community by Lior Bragilevsky (@lbragile).</description>
    <link>https://dev.to/lbragile</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%2F601256%2Fbfd1f661-f143-4e35-8550-41e22ba63929.jpeg</url>
      <title>DEV Community: Lior Bragilevsky</title>
      <link>https://dev.to/lbragile</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lbragile"/>
    <language>en</language>
    <item>
      <title>useReducer + useContext + Typescript. Without Redux!</title>
      <dc:creator>Lior Bragilevsky</dc:creator>
      <pubDate>Sat, 29 Jan 2022 04:19:18 +0000</pubDate>
      <link>https://dev.to/lbragile/usereducer-usecontext-typescript-without-redux-56k</link>
      <guid>https://dev.to/lbragile/usereducer-usecontext-typescript-without-redux-56k</guid>
      <description>&lt;p&gt;Ever find yourself questioning why it is that you need to use a library like &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; when &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; already has this functionality in the form of hooks?&lt;/p&gt;

&lt;p&gt;That's right, React comes with 2 hooks that can be leveraged to reproduce Redux-like functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useReducer&lt;/code&gt; is an "alternative" &lt;code&gt;useState&lt;/code&gt; that is often used &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This sounds pretty useful for the reducer portion of Redux right?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useContext&lt;/code&gt; allows you to pass information (state in our case) between components even if they are not direct siblings. This avoids a well known side-effect - &lt;a href="https://kentcdodds.com/blog/prop-drilling" rel="noopener noreferrer"&gt;prop drilling&lt;/a&gt; - making it easier to scale your codebase since there is a "global store" (just like in Redux 😃)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Wait, what about typing? Doesn't Redux already handle all of this for us with their wonderful &lt;code&gt;combineReducers&lt;/code&gt; generic?&lt;/p&gt;

&lt;p&gt;Yes, but that requires 2 extra modules (Redux &amp;amp; React-Redux) for a "simple" function - &lt;code&gt;node_modules&lt;/code&gt; is already large enough. &lt;/p&gt;

&lt;p&gt;Also, wouldn't you feel better as a developer if you actually knew what is going on behind the scene? Or dare I say, how to actually type it yourself?&lt;/p&gt;

&lt;p&gt;Those were trick questions, the answer to both is &lt;strong&gt;yes&lt;/strong&gt; and you will learn a bunch by taking &lt;em&gt;action&lt;/em&gt; and &lt;em&gt;reducing&lt;/em&gt; the number of modules you use in your project 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample Repository &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You can see the full codebase for what I am about to share in my recent project:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lbragile" rel="noopener noreferrer"&gt;
        lbragile
      &lt;/a&gt; / &lt;a href="https://github.com/lbragile/TabMerger" rel="noopener noreferrer"&gt;
        TabMerger
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      TabMerger is a cross-browser extension that stores your tabs in a single place to save memory usage and increase your productivity.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://raw.githubusercontent.com/lbragile/TabMerger/master/public/images/logo-full.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Flbragile%2FTabMerger%2Fmaster%2Fpublic%2Fimages%2Flogo-full.png" alt="tabmerger logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lbragile/TabMerger/actions" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/071a35f8e84ee4f21291854e91eae623b71513bcd1f9f69d608ee4eed467b4d6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f6c62726167696c652f5461624d65726765722f5461624d6572676572253230556e697425323054657374696e673f6c6162656c3d4275696c64267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="Build"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/network/members" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3e7e80fa00d5cc0abec93b19c2b7f5ac05620ade045c4b6b6ceb5e086e32da0e/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f6c62726167696c652f5461624d65726765723f6c6162656c3d466f726b73266c6f676f3d676974687562267374796c653d666c61742d737175617265" alt="Forks"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/stargazers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/e1896f2bd9b811369e910e99343a460782607d3f142219c5e5a75a2cf1a337fe/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6c62726167696c652f5461624d65726765723f6c6162656c3d5374617273267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="Stars"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/watchers" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d7570fb6952b68ae3a5ac529005bdb91bff06c48abe30892cb63563e8d954440/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f77617463686572732f6c62726167696c652f5461624d65726765723f6c6162656c3d5761746368657273266c6f676f3d676974687562267374796c653d666c61742d737175617265" alt="Watchers"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/releases/tag/v2.0.0" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2912c3d819d18573afe7c5fc1ad45c196cc87580d1cac8678fb4f27e355fd58a/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6c62726167696c652f5461624d65726765723f6c6162656c3d52656c65617365266c6f676f3d476974687562267374796c653d666c61742d737175617265" alt="Release"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/blob/master/LICENSE.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5a0dca4b7bd3c263c946326e5833edc9cc055eae8fd934e2ed26a65d456d84bc/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6c62726167696c652f7461626d65726765723f6c6162656c3d4c6963656e7365267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="License"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f12c54562168240977cda9238566114d9f62f8c3954e26f0bdbc5bd822c50031/68747470733a2f2f696d672e736869656c64732e696f2f6368726f6d652d7765622d73746f72652f75736572732f696e6d69616a6170627061666d686a6c6569656263616d66686b666e6c676f633f6c6162656c3d5573657273267374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Chrome Users"&gt;&lt;/a&gt; &lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d124ad531bb95b51ce01f40f8155069972701394f623120f85fe67b722153fb8/68747470733a2f2f696d672e736869656c64732e696f2f6368726f6d652d7765622d73746f72652f726174696e672f696e6d69616a6170627061666d686a6c6569656263616d66686b666e6c676f633f6c6162656c3d526174696e67267374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Chrome Rating"&gt;&lt;/a&gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tabmerger/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/3ea2ff6fcc926f142953f9bc2230f06a649bff6435500eb02ba499f80dc88f72/68747470733a2f2f696d672e736869656c64732e696f2f616d6f2f75736572732f25374231396665623834662d336130622d346361332d626261652d3231316235326562313538622537443f6c6162656c3d5573657273267374796c653d666c61742d737175617265266c6f676f3d66697265666f78" alt="Firefox Users"&gt;&lt;/a&gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tabmerger/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/82beeb058f6ef4b1a4f95469be179b853d14c0818b2fd3411745d1eecb7bba8b/68747470733a2f2f696d672e736869656c64732e696f2f616d6f2f726174696e672f253235374231396665623834662d336130622d346361332d626261652d32313162353265623135386225323537443f6c6162656c3d526174696e67267374796c653d666c61742d737175617265266c6f676f3d66697265666f78" alt="Firefox Rating"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="http://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/76f321bb3abbfd866ebe0dddf782b5f0ff09e8ed6280f4302f8f0669a5265c6f/68747470733a2f2f692e696d6775722e636f6d2f4e4b4674774f412e706e67" alt="Chrome WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://addons.mozilla.org/en-CA/firefox/addon/tabmerger/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/860d4213e6c5372de8feeea5cd3ef1eda284a125fba041c2b99fae8442a5ef74/68747470733a2f2f692e696d6775722e636f6d2f59547a373237652e706e67" alt="Firefox WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://microsoftedge.microsoft.com/addons/detail/tabmerger/eogjdfjemlgmbblgkjlcgdehbeoodbfn" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/8130203f138b3868efc1a29458d2d2d3238e7d8137a45a54b04b0dfee0ada8d5/68747470733a2f2f692e696d6775722e636f6d2f595152325259642e706e67" alt="Edge WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/tabmerger?utm_source=badge-featured&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-tabmerger" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/f37491d800f317fb7dd9a3e4607e919728c2da0a9369158c2923c219bf781e68/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f66656174757265642e7376673f706f73745f69643d323833363832267468656d653d6c69676874" alt="Product Hunt"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Stores your tabs in one location to save memory usage and increase your productivity&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e614353230d65513988a01aab8af35c78fb304cd444e12408d22e704b43a20e8/68747470733a2f2f692e696d6775722e636f6d2f327362457a69702e706e673f31"&gt;&lt;img src="https://camo.githubusercontent.com/e614353230d65513988a01aab8af35c78fb304cd444e12408d22e704b43a20e8/68747470733a2f2f692e696d6775722e636f6d2f327362457a69702e706e673f31" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🖋 Description&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Tired of searching through squished icons to find a tab you are sure is there?&lt;/p&gt;
&lt;p&gt;TabMerger simplifies this clutter while increasing productivity in a highly organized and customizable fashion!&lt;/p&gt;
&lt;p&gt;In one click, you can have everything in a common location, where you can then re-arrange into appropriate groups, add custom notes, and so much more
All items are stored internally for you to use at a later time, even when you close the browser window(s) - reducing memory consumption and speeding up your machine.
Lots of analytics keep you informed.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;⭐ Review&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;If you found TabMerger useful, consider leaving a positive &amp;amp; meaningful review (&lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc/reviews" rel="nofollow noopener noreferrer"&gt;Chrome&lt;/a&gt; | &lt;a href="https://addons.mozilla.org/en-CA/firefox/addon/tabmerger/" rel="nofollow noopener noreferrer"&gt;Firefox&lt;/a&gt; | &lt;a href="https://microsoftedge.microsoft.com/addons/detail/tabmerger/eogjdfjemlgmbblgkjlcgdehbeoodbfn" rel="nofollow noopener noreferrer"&gt;Edge&lt;/a&gt;)
&lt;br&gt;
It would also mean a lot if you could 🌟 this repository on &lt;a href="https://www.github.com/lbragile/TabMerger" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;!&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;💸 Donate&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;I would greatly appreciate any financial…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lbragile/TabMerger" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  📑 Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Redux In A Nutshell&lt;/li&gt;
&lt;li&gt;
Root State, Actions &amp;amp; Reducers Magic

&lt;ul&gt;
&lt;li&gt;State&lt;/li&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;li&gt;Reducers&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Store Provider&lt;/li&gt;
&lt;li&gt;
useSelector &amp;amp; useDispatch

&lt;ul&gt;
&lt;li&gt;useSelector&lt;/li&gt;
&lt;li&gt;useDispatch&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Bonus - useReducerLogger&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🥜 Redux In A Nutshell &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As you should know, reducers are functions that essentially start with some initial state and, based on the &lt;code&gt;action.type&lt;/code&gt; and/or &lt;code&gt;action.payload&lt;/code&gt;, update said state.&lt;/p&gt;

&lt;p&gt;For example (ignore the typing for now):&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="c1"&gt;// src/store/reducers/dnd.ts&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;TRootActions&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;~/typings/reducers&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;DND_ACTIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;UPDATE_DRAG_ORIGIN_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;UPDATE_DRAG_ORIGIN_TYPE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;UPDATE_IS_DRAGGING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UPDATE_IS_DRAGGING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;RESET_DND_INFO&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RESET_DND_INFO&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&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;IDnDState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;dragType&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="nl"&gt;isDragging&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="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;initDnDState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IDnDState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;dragType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tab-0-window-0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isDragging&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dndReducer&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="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initDnDState&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;TRootActions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;IDnDState&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;action&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="nx"&gt;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UPDATE_DRAG_ORIGIN_TYPE&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;dragType&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="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UPDATE_IS_DRAGGING&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;isDragging&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="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RESET_DND_INFO&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;initDnDState&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="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="nx"&gt;dndReducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As your project grows, you will have multiple reducers for different stages - these are known as &lt;strong&gt;slices&lt;/strong&gt; in Redux. In TabMerger's case, I created reducers for &lt;code&gt;dnd&lt;/code&gt; (saw above), &lt;code&gt;header&lt;/code&gt;, &lt;code&gt;groups&lt;/code&gt;, and &lt;code&gt;modal&lt;/code&gt; - for a total of 4 slices.&lt;/p&gt;

&lt;p&gt;Redux provides a way to dispatch actions which use these reducers. Guess what, &lt;code&gt;useReducer&lt;/code&gt; does also... in fact, it is the second element in the array that gets destructured:&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="c1"&gt;// rootReducer and rootState are not defined yet...&lt;/span&gt;
&lt;span class="c1"&gt;// ... I show them here for context&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;state&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;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Side Note&lt;/strong&gt;: &lt;code&gt;useReducer&lt;/code&gt; is actually a generic hook, but if you type everything properly (as I will show below) it's type will be inferred based on the arguments provided.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;dispatch&lt;/code&gt; acts similarly to the &lt;code&gt;setState&lt;/code&gt; of a &lt;code&gt;useState&lt;/code&gt; hook, and you supply the action object which is consumed in the reducer. For example:&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="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&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;DND_ACTIONS.UPDATE_IS_DRAGGING&lt;/span&gt;&lt;span class="dl"&gt;"&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="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="c1"&gt;// more code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it is common practice to also make "Action Creators" for each reducer &lt;em&gt;case&lt;/em&gt;, to simplify the above &lt;code&gt;dispatch&lt;/code&gt; call. These action creators are just "wrappers" that return the expected type and payload object and allow you to simply call the function and pass the payload as needed. For example:&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="c1"&gt;// src/store/actions/dnd.ts&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;DND_ACTIONS&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;~/store/reducers/dnd&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;updateDragOriginType&lt;/span&gt; &lt;span class="o"&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;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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPDATE_DRAG_ORIGIN_TYPE&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateIsDragging&lt;/span&gt; &lt;span class="o"&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="nx"&gt;boolean&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;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UPDATE_IS_DRAGGING&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resetDnDInfo&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="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DND_ACTIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESET_DND_INFO&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can call:&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="c1"&gt;// some code&lt;/span&gt;
&lt;span class="p"&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;updateIsDragging&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="c1"&gt;// more code&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neat right?&lt;/p&gt;

&lt;p&gt;This is the reasoning behind making the &lt;code&gt;DND_ACTIONS&lt;/code&gt; object - you specify your types in one place and then your IDE can help with auto completion, which prevents you from making grammatical mistakes that can lead to bugs.&lt;/p&gt;

&lt;p&gt;You are probably wondering, why the &lt;code&gt;as const&lt;/code&gt; casting for the &lt;code&gt;DND_ACTIONS&lt;/code&gt; object?&lt;/p&gt;

&lt;p&gt;This is to provide typescript with strict typing in our action creators. Without the casting, each value in the object will have a general &lt;strong&gt;string&lt;/strong&gt; type. With the casting, each value will be &lt;em&gt;readonly&lt;/em&gt; and exactly the value we specify. This allow TypeScript to deduce what the payload type is for each case in our reducer function since the action creator "type" property value is exactly matching and not just a generic string value.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎉 Root State, Actions &amp;amp; Reducers Magic &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Those that are keen, would have noticed that in addition to exporting the reducer (&lt;strong&gt;default export&lt;/strong&gt;), I also exported the initial state as a &lt;strong&gt;named export&lt;/strong&gt;. Again, this is done for all slices.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;As discussed above, we need to combine these reducers right?&lt;/p&gt;

&lt;p&gt;Well, to do this, we also need to combine the initial state "slices".&lt;/p&gt;

&lt;p&gt;Here is how (step by step analysis follows):&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="c1"&gt;// src/store/index.ts&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dndActions&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;../actions/dnd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;groupsActions&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;../actions/groups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;headerActions&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;../actions/header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;modalActions&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;../actions/modal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dndReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initDnDState&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;./dnd&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;groupsReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initGroupsState&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;./groups&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;headerReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initHeaderState&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;./header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;modalReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initModalState&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;./modal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;ReducersMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TRootState&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;~/typings/reducers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Takes in reducer slices object and forms a single reducer with the combined state as output
 * @see https://stackoverflow.com/a/61439698/4298115
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;combineReducers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootState&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;reducers&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;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&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;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Build the combined state&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;S&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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="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="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;rootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initHeaderState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initGroupsState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initDnDState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initModalState&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;rootActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;headerActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;groupsActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dndActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modalActions&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;rootReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combineReducers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;headerReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;groupsReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dndReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modalReducer&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and here is the corresponding typing for each:&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="c1"&gt;// src/typings/redux.d.ts&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;Reducer&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;rootActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootState&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;~/store&lt;/span&gt;&lt;span class="dl"&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;ActionsMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;infer&lt;/span&gt; &lt;span class="nx"&gt;R&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;R&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;rootState&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ActionsMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;rootActions&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootActions&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;Reducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;A&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;h3&gt;
  
  
  🔬 Analysis &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Lets break down the above as there is quite a bit of information there and it is the most critical part for avoiding Redux completely.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. State &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;rootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initHeaderState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initGroupsState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initDnDState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initModalState&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;rootState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "root state" is easiest to form as it is just an object with the slices as keys and the initial state values (exported from the reducers) as the corresponding value.&lt;/p&gt;

&lt;p&gt;The type of the "root state" is also simple, as it is just the type of this object.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Actions &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;rootActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;headerActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;groupsActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dndActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modalActions&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;type&lt;/span&gt; &lt;span class="nx"&gt;ActionsMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;infer&lt;/span&gt; &lt;span class="nx"&gt;R&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;R&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ActionsMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;rootActions&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;The "root actions" is a again just the keys of each slice, with the corresponding combined (&lt;code&gt;import * as value from "..."&lt;/code&gt;) imported action creators object.&lt;/p&gt;

&lt;p&gt;Its type is a bit more involved.&lt;/p&gt;

&lt;p&gt;We want our reducers' action argument to contain all possible action creator types so that when we use a value for the &lt;code&gt;action.type&lt;/code&gt;, TypeScript can cross reference all the action creators to find the correct payload typing for this &lt;code&gt;action.type&lt;/code&gt;. Obviously each &lt;code&gt;action.type&lt;/code&gt; should be unique for this to work properly. To do this, we generate a &lt;em&gt;union&lt;/em&gt; type consisting of the return types of each of the action creators:&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="nl"&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;UPDATE_DRAG_ORIGIN_TYPE&lt;/span&gt;&lt;span class="dl"&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;string&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="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;UPDATE_IS_DRAGGING&lt;/span&gt;&lt;span class="dl"&gt;"&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;boolean&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="o"&gt;|&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;same&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;each&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice, how the type of the "type" property is not just &lt;code&gt;string&lt;/code&gt;, but rather the exact value provided in the &lt;code&gt;DND_ACTIONS&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Currently the "root actions" object looks something like:&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="c1"&gt;// typeof rootActions&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;header&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;headerActions&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;groups&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;groupsActions&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;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;updateDragOriginType&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;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="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;UPDATE_DRAG_ORIGIN_TYPE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="nl"&gt;payload&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="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;updateIsDragging&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="nx"&gt;boolean&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;UPDATE_IS_DRAGGING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;payload&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="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;resetDnDInfo&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="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;RESET_DND_INFO&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="nx"&gt;modal&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;modalActions&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;So we need to use the following mapped type:&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ActionsMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;A&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="na"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&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;infer&lt;/span&gt; &lt;span class="nx"&gt;R&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;R&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;never&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This maps each slice in "root actions" and checks if it's value's type is an object that contains the key/value pair where the value is a function with any number of arguments of any type. If it is, then we set the return type of that &lt;em&gt;value&lt;/em&gt; function to &lt;code&gt;R&lt;/code&gt; (whatever it is) and return it. Otherwise we return &lt;code&gt;never&lt;/code&gt;. Lastly, as we still have an object (&lt;code&gt;Record&amp;lt;[slice], [union of slice's action creator return types]&amp;gt;&lt;/code&gt;) we use &lt;code&gt;[keyof A]&lt;/code&gt; to create a union of these slices - producing the desired type.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Reducers &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Finally, what I consider the most challenging is the combined reducers.&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;combineReducers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootState&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;reducers&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;K&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;K&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;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="o"&gt;&amp;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="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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Build the combined state&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="nx"&gt;S&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;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nx"&gt;prevState&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&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="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="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;rootReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combineReducers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;headerReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;groupsReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dnd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dndReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;modal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;modalReducer&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;type&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRootActions&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;Reducer&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;A&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;First, the &lt;code&gt;combineReducers&lt;/code&gt; generic is a function that takes in the "root reducer" object (separated into slices as with state and action creators) and, as the name implies, combines them into a properly typed, single reducer. This is accomplished by looping over the slices and forming the combined state via JavaScript's &lt;code&gt;Array.prototype.reduce()&lt;/code&gt;. Then the "root reducer" is simply a function that, as with any other reducer, takes a state (&lt;code&gt;rootState&lt;/code&gt;) and action (&lt;code&gt;rootActions&lt;/code&gt;) as arguments and returns a new "root state".&lt;/p&gt;

&lt;p&gt;The typing for the "root reducer" is simple and just leverages React's built in &lt;code&gt;Reducer&lt;/code&gt; generic. By default, I pass the &lt;code&gt;TRootState&lt;/code&gt; and &lt;code&gt;TRootActions&lt;/code&gt; to it. For the argument to the &lt;code&gt;combineReducers&lt;/code&gt; we need to supply the reducer corresponding to each slice. This is accomplished via a mapped type for each slice from the "state" argument (generally &lt;code&gt;TRootState&lt;/code&gt;) to the corresponding reducer. Note that the action type stays the union of all action creators for each slice as it is assumed that &lt;code&gt;action.type&lt;/code&gt; is globally unique across all reducers.&lt;/p&gt;

&lt;p&gt;Now that we got the tough part out of the way, lets set up our store!&lt;/p&gt;

&lt;h2&gt;
  
  
  🏪 Store Provider &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Redux has a handy &lt;a href="https://react-redux.js.org/api/provider" rel="noopener noreferrer"&gt;Provider&lt;/a&gt; into which you pass your state (store) and the whole app can use it.&lt;/p&gt;

&lt;p&gt;This can be accomplished with &lt;code&gt;useContext&lt;/code&gt; and the state (along with the dispatch) can be created with &lt;code&gt;useReducer&lt;/code&gt; as mentioned previously.&lt;/p&gt;

&lt;p&gt;Here is TabMerger's &lt;code&gt;StoreProvider&lt;/code&gt; component:&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="c1"&gt;// src/store/configureStore.tsx&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;createContext&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="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useReducer&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useReducerLogger&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;~/hooks/useReducerLogger&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootState&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;~/store/reducers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;TRootActions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TRootState&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;~/typings/reducers&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;ReduxStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&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;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRootState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TRootActions&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;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;dispatch&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="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;StoreProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Element&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;loggedReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducerLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootReducer&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;state&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;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;development&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;loggedReducer&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rootState&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;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;state&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="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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ReduxStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&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;children&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;/ReduxStore.Provider&amp;gt;&lt;/span&gt;&lt;span class="err"&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="nx"&gt;StoreProvider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is done here?&lt;/p&gt;

&lt;p&gt;A global context is created - &lt;code&gt;ReduxStore&lt;/code&gt; - using React's &lt;code&gt;createContext&lt;/code&gt; generic and is set with non important default values (can be anything as long as the typing makes sense). This context is typed to be an object with state (&lt;code&gt;TRootState&lt;/code&gt;) and dispatch (&lt;code&gt;React.Dispatch&amp;lt;TRootActions&amp;gt;&lt;/code&gt;) properties. &lt;/p&gt;

&lt;p&gt;The component itself takes a &lt;code&gt;children&lt;/code&gt; prop (since it will wrap our entire app) and uses &lt;code&gt;useReducer&lt;/code&gt; to create the &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;dispatch&lt;/code&gt; values that will be passed to the context created above (and used throughout the app). The &lt;code&gt;useReducer&lt;/code&gt; takes either a logging root reducer (see bonus section) or a regular root reducer depending on the environment and the root state as arguments. Due to the previous typing for both arguments, the &lt;code&gt;useReducer&lt;/code&gt; can infer the respective types and thus does not need to be typed additionally.&lt;/p&gt;

&lt;p&gt;Next the context object is memoized with &lt;code&gt;useMemo&lt;/code&gt; to avoid redundant re-renders of all components. Finally, the memoized value is passed to the provider for the "children" (our app) to consume.&lt;/p&gt;

&lt;h2&gt;
  
  
  🦄 useSelector &amp;amp; useDispatch &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Redux also has &lt;code&gt;useSelector&lt;/code&gt; and &lt;code&gt;useDispatch&lt;/code&gt; hooks which can be easily created with our new context, saving us from having to import the context each time.&lt;/p&gt;

&lt;h3&gt;
  
  
  useSelector &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;useSelector&lt;/code&gt; hook simply takes a callback function which returns a specific state item from the "root state" object.&lt;/p&gt;

&lt;p&gt;For example, to retrieve the &lt;code&gt;isDragging&lt;/code&gt; property from the &lt;code&gt;dnd&lt;/code&gt; state item, we can do:&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;isDragging&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&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;dnd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to make this? How to type this? Let's see:&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="c1"&gt;// src/hooks/useRedux.ts&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;useContext&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;ReduxStore&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;~/store/configureStore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;TRootState&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;~/typings/reducers&lt;/span&gt;&lt;span class="dl"&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;TypedUseSelectorHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;U&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;cb&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;TRootState&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;U&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;U&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;useSelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TypedUseSelectorHook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cb&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;state&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ReduxStore&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;cb&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;As can be seen, the &lt;code&gt;useSelector&lt;/code&gt; is just a function which takes a callback as an argument. We retrieve the state from our context, and pass it to the callback - which extracts the needed item in our codebase as shown in the above example.&lt;/p&gt;

&lt;p&gt;To type the &lt;code&gt;useSelector&lt;/code&gt; we let TypeScript do its thing by "inferring" the return type of whatever callback we pass to it, storing it in &lt;code&gt;U&lt;/code&gt; and then setting the return of the &lt;code&gt;useSelector&lt;/code&gt; to match this type (&lt;code&gt;U&lt;/code&gt;). This ensures proper typing throughout our app.&lt;/p&gt;

&lt;h3&gt;
  
  
  useDispatch &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;useDispatch&lt;/code&gt; hook is even simpler as it can just return our context's dispatch function:&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="c1"&gt;// src/hooks/useRedux.ts&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;useDispatch&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;dispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ReduxStore&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;dispatch&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 dispatch function will be properly typed as it comes from the typed context (&lt;code&gt;ReduxStore&lt;/code&gt;). It can then be called inside any component as follows:&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;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="p"&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;updateIsDragging&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🙌 Bonus - useReducerLogger &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As seen above, in development mode, I use a &lt;code&gt;useReducerLogger&lt;/code&gt; custom hook to log each dispatched action - based on the &lt;a href="https://www.npmjs.com/package/redux-logger" rel="noopener noreferrer"&gt;Redux Logger&lt;/a&gt; npm package.&lt;/p&gt;

&lt;p&gt;Here is the logic for it:&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="c1"&gt;// src/hooks/useReducerLogger.ts&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;useCallback&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&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;TRootReducer&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;~/typings/reducers&lt;/span&gt;&lt;span class="dl"&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;getTimestamp&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;d&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;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Need to zero pad each value&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;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&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;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSeconds&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMilliseconds&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="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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;const&lt;/span&gt; &lt;span class="nx"&gt;getStyle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;color&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="s2"&gt;`color: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;; font-weight: 600;`&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useReducerLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TRootReducer&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;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&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="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;nextState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prevState&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groupCollapsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`%c action %c&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="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; %c@ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;getTimestamp&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nf"&gt;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#9e9e9e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;initial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nf"&gt;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#9e9e9e&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%c prev 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;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#9e9e9e&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;prevState&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;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%c action&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#00a7f7&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;%c next 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;getStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#47b04b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;nextState&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;groupEnd&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;nextState&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;reducer&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;This hook simply uses console groups to create collapsed groups that contain the necessary information in each dispatch. This hook is also memoized to re-render only when a the root reducer changes (state or dispatch)&lt;/p&gt;

&lt;h2&gt;
  
  
  🏁 Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The key takeaways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redux's core functionality can be re-created with &lt;code&gt;useReducer&lt;/code&gt; &amp;amp; &lt;code&gt;useContext&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Helper hooks (abstractions), like &lt;code&gt;useSelector&lt;/code&gt; and &lt;code&gt;useDispatch&lt;/code&gt; are relatively simple to create&lt;/li&gt;
&lt;li&gt;Typescript (when used correctly) can provide an incredible developer experience&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;as const&lt;/code&gt; is helpful for instances where strong typing is required - as in action creators. Without it, there would be no way to deduce each action's payload typing based on the &lt;code&gt;action.type&lt;/code&gt; (since the action's type will be inferred as &lt;code&gt;string&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Mapped types paired with &lt;code&gt;infer&lt;/code&gt; are extremely useful when working with data whose type is not known in advance - such as the payload in action creators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't get me wrong, Redux is great! However, I think it is much more empowering (as a developer) when you have full control of &lt;em&gt;everything&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Leveraging React's &lt;code&gt;useContext&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt; is a great way to completely eliminate Redux. Typescript comes to the rescue if you also want your codebase to be strongly typed - I highly recommend this as it prevent careless errors.&lt;/p&gt;

&lt;p&gt;If you feel inspired and/or find TabMerger interesting, feel free to contribute as it is open source 🙂&lt;/p&gt;

&lt;p&gt;Cheers 🥂&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>redux</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 things to include in your project ASAP</title>
      <dc:creator>Lior Bragilevsky</dc:creator>
      <pubDate>Mon, 22 Mar 2021 01:03:08 +0000</pubDate>
      <link>https://dev.to/lbragile/5-things-to-include-in-your-project-asap-2447</link>
      <guid>https://dev.to/lbragile/5-things-to-include-in-your-project-asap-2447</guid>
      <description>&lt;p&gt;After the launch of &lt;strong&gt;v2.0&lt;/strong&gt; of my current project (TabMerger), I decided to learn/integrate a few items that really pushed my skills to the next level. Best of all, adding these to my projects made me very excited to work on new projects and/or refactor existing ones.&lt;/p&gt;

&lt;p&gt;Here is TabMerger's repository which you can view to get ideas about how to add any of the features discussed below.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/lbragile"&gt;
        lbragile
      &lt;/a&gt; / &lt;a href="https://github.com/lbragile/TabMerger"&gt;
        TabMerger
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      TabMerger is a cross-browser extension that stores your tabs in a single place to save memory usage and increase your productivity.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://raw.githubusercontent.com/lbragile/TabMerger/master/public/images/logo-full.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Tk_Oa80g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/lbragile/TabMerger/master/public/images/logo-full.png" alt="tabmerger logo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/lbragile/TabMerger/actions"&gt;&lt;img src="https://camo.githubusercontent.com/ecfa3d4215579a786dc9f64236402f2cfa32960961891dbbbaa2200e46258c89/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f6c62726167696c652f5461624d65726765722f5461624d6572676572253230556e697425323054657374696e673f6c6162656c3d4275696c64267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="Build"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/network/members"&gt;&lt;img src="https://camo.githubusercontent.com/b4fb988352b136885da94fd21eb5e75a680a0384e1d3cbf86829f984ad8318d2/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f666f726b732f6c62726167696c652f5461624d65726765723f6c6162656c3d466f726b73266c6f676f3d676974687562267374796c653d666c61742d737175617265" alt="Forks"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/stargazers"&gt;&lt;img src="https://camo.githubusercontent.com/b9a89f8e0219c80b709b593ea5b4b3324f48c854609be87169cbe4d4fdf3e2eb/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f73746172732f6c62726167696c652f5461624d65726765723f6c6162656c3d5374617273267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="Stars"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/watchers"&gt;&lt;img src="https://camo.githubusercontent.com/a2aa67d926e5c2666fdb5ce949530d3d02b8dd03af25cd183bbdacd5867c125c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f77617463686572732f6c62726167696c652f5461624d65726765723f6c6162656c3d5761746368657273266c6f676f3d676974687562267374796c653d666c61742d737175617265" alt="Watchers"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/releases/tag/v2.0.0"&gt;&lt;img src="https://camo.githubusercontent.com/62802b4bc519ce625a52913170374b0ccef0e1f3d47661cf5a113da4068d375d/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f72656c656173652f6c62726167696c652f5461624d65726765723f6c6162656c3d52656c65617365266c6f676f3d476974687562267374796c653d666c61742d737175617265" alt="Release"&gt;&lt;/a&gt; &lt;a href="https://github.com/lbragile/TabMerger/blob/master/LICENSE.md"&gt;&lt;img src="https://camo.githubusercontent.com/822ddefd0a41f6dd638fb7609af9c25bea3948c75435388c324e557dbaf49a38/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f6c62726167696c652f7461626d65726765723f6c6162656c3d4c6963656e7365267374796c653d666c61742d737175617265266c6f676f3d676974687562" alt="License"&gt;&lt;/a&gt;
&lt;br&gt;
&lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/da18f7f260444787be37e414dab67b8c86b772ee589ba2dc042fcb6c48954241/68747470733a2f2f696d672e736869656c64732e696f2f6368726f6d652d7765622d73746f72652f75736572732f696e6d69616a6170627061666d686a6c6569656263616d66686b666e6c676f633f6c6162656c3d5573657273267374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Chrome Users"&gt;&lt;/a&gt; &lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c416347fabe0ab79ac9acd60b8577bebbfe17ca0e9b9cf443e2262b695900b0d/68747470733a2f2f696d672e736869656c64732e696f2f6368726f6d652d7765622d73746f72652f726174696e672f696e6d69616a6170627061666d686a6c6569656263616d66686b666e6c676f633f6c6162656c3d526174696e67267374796c653d666c61742d737175617265266c6f676f3d676f6f676c65266c6f676f436f6c6f723d7768697465" alt="Chrome Rating"&gt;&lt;/a&gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tabmerger/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/c8dc276845a5ec9701f7446b62ed92001edb8716d2a4a1ec0a32c7ac24c42b70/68747470733a2f2f696d672e736869656c64732e696f2f616d6f2f75736572732f25374231396665623834662d336130622d346361332d626261652d3231316235326562313538622537443f6c6162656c3d5573657273267374796c653d666c61742d737175617265266c6f676f3d66697265666f78" alt="Firefox Users"&gt;&lt;/a&gt; &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tabmerger/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/6fabeef710a72f276c72bca48417dc3a25a74dd3250d6850a3c814f182573c8c/68747470733a2f2f696d672e736869656c64732e696f2f616d6f2f726174696e672f253235374231396665623834662d336130622d346361332d626261652d32313162353265623135386225323537443f6c6162656c3d526174696e67267374796c653d666c61742d737175617265266c6f676f3d66697265666f78" alt="Firefox Rating"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;a href="http://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ecd3dcd0bf48c33ba76a2c0b2bc135c40b612f2110e63b65192c6e03dac5b9b5/68747470733a2f2f692e696d6775722e636f6d2f4e4b4674774f412e706e67" alt="Chrome WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://addons.mozilla.org/en-CA/firefox/addon/tabmerger/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8ac769dbf40305702ea67e9f645468c153f5fe96d7501e84f439d42b1e6673ec/68747470733a2f2f692e696d6775722e636f6d2f59547a373237652e706e67" alt="Firefox WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;th&gt;&lt;a href="https://microsoftedge.microsoft.com/addons/detail/tabmerger/eogjdfjemlgmbblgkjlcgdehbeoodbfn" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8bf6c3c8daab09b538bfe815f0cb70bd4d53ba9a0be1fb16cfb0dd4292c5edfb/68747470733a2f2f692e696d6775722e636f6d2f595152325259642e706e67" alt="Edge WebStore"&gt;&lt;/a&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href="https://www.producthunt.com/posts/tabmerger?utm_source=badge-featured&amp;amp;utm_medium=badge&amp;amp;utm_souce=badge-tabmerger" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/25f0745f8670b93253ca13a85c161b6eb7203de9552f682665f89e27be8cdb0a/68747470733a2f2f6170692e70726f6475637468756e742e636f6d2f776964676574732f656d6265642d696d6167652f76312f66656174757265642e7376673f706f73745f69643d323833363832267468656d653d6c69676874" alt="Product Hunt"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
Stores your tabs in one location to save memory usage and increase your productivity&lt;/h1&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://camo.githubusercontent.com/17cdd786564d4f445f55882706e452e4d6310f19854312ab5c724f405fccce38/68747470733a2f2f692e696d6775722e636f6d2f327362457a69702e706e673f31"&gt;&lt;img src="https://camo.githubusercontent.com/17cdd786564d4f445f55882706e452e4d6310f19854312ab5c724f405fccce38/68747470733a2f2f692e696d6775722e636f6d2f327362457a69702e706e673f31" alt="Demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;
🖋 Description&lt;/h2&gt;
&lt;p&gt;Tired of searching through squished icons to find a tab you are sure is there?&lt;/p&gt;
&lt;p&gt;TabMerger simplifies this clutter while increasing productivity in a highly organized and customizable fashion!&lt;/p&gt;
&lt;p&gt;In one click, you can have everything in a common location, where you can then re-arrange into appropriate groups, add custom notes, and so much more
All items are stored internally for you to use at a later time, even when you close the browser window(s) - reducing memory consumption and speeding up your machine
Lots of analytics keep you informed.&lt;/p&gt;
&lt;h2&gt;
⭐ Review&lt;/h2&gt;
&lt;p&gt;If you found TabMerger useful, consider leaving a positive &amp;amp; meaningful review (&lt;a href="https://chrome.google.com/webstore/detail/tabmerger/inmiajapbpafmhjleiebcamfhkfnlgoc/reviews" rel="nofollow"&gt;Chrome&lt;/a&gt; | &lt;a href="https://addons.mozilla.org/en-CA/firefox/addon/tabmerger/" rel="nofollow"&gt;Firefox&lt;/a&gt; | &lt;a href="https://microsoftedge.microsoft.com/addons/detail/tabmerger/eogjdfjemlgmbblgkjlcgdehbeoodbfn" rel="nofollow"&gt;Edge&lt;/a&gt;)
&lt;br&gt;
It would also mean a lot if you could 🌟 this repository on &lt;a href="https://www.github.com/lbragile/TabMerger"&gt;GitHub&lt;/a&gt;!&lt;/p&gt;
&lt;h2&gt;
💸 Donate&lt;/h2&gt;
&lt;p&gt;I would greatly appreciate any financial…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/lbragile/TabMerger"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Here are the concepts I urge you to learn as they will hopefully bring the same excitement into your coding life - don't fall into the trap of pushing them off/procrastinating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents 📑
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Testing&lt;/li&gt;
&lt;li&gt;
Linting - Static Testing

&lt;ul&gt;
&lt;li&gt;Source Code Linting&lt;/li&gt;
&lt;li&gt;Style Sheet Linting&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Module Aliasing&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Testing 🧪 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I highly recommend &lt;a href="https://jestjs.io/"&gt;Jest&lt;/a&gt; as it is available right out of the box when you use React (CRA), but you could also use other test runners like &lt;a href="https://mochajs.org/"&gt;Mocha&lt;/a&gt;, &lt;a href="https://karma-runner.github.io/latest/index.html"&gt;Karma&lt;/a&gt;, etc.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why?
&lt;/h4&gt;

&lt;p&gt;Do you want to manually test every little feature of your code &lt;strong&gt;every&lt;/strong&gt; time you change/update/add something?&lt;/p&gt;

&lt;p&gt;Yeah, no thanks, I would rather have a testing script that &lt;u&gt;automates&lt;/u&gt; this for me. Plus it is super rewarding once you understand the main concepts. This is probably the &lt;em&gt;most&lt;/em&gt; time consuming of all the items listed here.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Start with the basics - &lt;a href="https://en.wikipedia.org/wiki/Unit_testing"&gt;Unit Testing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Look into &lt;a href="https://en.wikipedia.org/wiki/Mutation_testing"&gt;Mutation Testing&lt;/a&gt; - this is insanely amazing once you understand how to use it! &lt;a href="https://stryker-mutator.io/docs/"&gt;Stryker&lt;/a&gt; is the way to go here.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Once you understand your coverage reports from Jest &amp;amp; Stryker, add &lt;a href="https://en.wikipedia.org/wiki/Integration_testing"&gt;Integration Tests&lt;/a&gt; and &lt;a href="https://www.tutorialspoint.com/software_testing_dictionary/end_to_end_testing.htm"&gt;E2E Tests&lt;/a&gt; with &lt;a href="https://jestjs.io/docs/puppeteer"&gt;Jest Puppeteer&lt;/a&gt; which is another easy to integrate module with React. &lt;br&gt;
&lt;u&gt;Disclaimer:&lt;/u&gt; I haven't done this step yet for TabMerger but experimented with this in the past and it is very fun - feel free to contribute 😊. This should be simpler than unit testing as it is "Black Box" since you do not care about the internal (just input and output) rather than a unit tests' "White Box" approach.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  TabMerger Testing
&lt;/h4&gt;

&lt;p&gt;Here is a brief snapshot of TabMerger's current testing performance:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IZxfAFM7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rcsoebt8r8nhsei51b6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IZxfAFM7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0rcsoebt8r8nhsei51b6.png" alt="TabMerger v2.0.0 testing performance" width="850" height="953"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, with these test scripts, I can check the logic of all the files in my application with the help of around &lt;strong&gt;250 tests&lt;/strong&gt; in less than &lt;strong&gt;20 seconds&lt;/strong&gt;. This gives me a great deal of confidence that new features do not break existing code. There is still some room for improvement (uncovered lines and not exactly 100%), but the current state lets me easily add new features without endlessly pursuing a 100% coverage report - after all 99.5% rounds up 😉.&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;npm run test:all&lt;/code&gt; to get these results.&lt;/p&gt;

&lt;p&gt;TabMerger also uses mutation testing and currently &lt;a href="https://dashboard.stryker-mutator.io/reports/github.com/lbragile/TabMerger/master"&gt;scores&lt;/a&gt; above &lt;strong&gt;95%&lt;/strong&gt; (only 67/1499 mutants are undetected across all files).&lt;/p&gt;

&lt;p&gt;I've parallelized the mutation testing scripts with a &lt;a href="https://github.com/lbragile/TabMerger/blob/master/.github/workflows/testing.yml#L49"&gt;matrix build&lt;/a&gt; in GitHub to speed up the lengthy execution - from 12 hours to 5 hours. &lt;/p&gt;

&lt;p&gt;As you can see from the below post, testing is a relatively "hidden" gem that many developers are not aware of or simply need the reason to get started. Additionally, almost all experienced testers recommended Stryker for mutation testing!&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__reddit--container"&gt;
  &lt;div class="ltag__reddit--title-container"&gt;
    
      &lt;div class="ltag__reddit--title"&gt;
        &lt;h1&gt;
          &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bCqI7Yj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/reddit-icon-c6851eed10026b5707e2e8c814b5bbcbb4823de68d5b611a6f4b99c8beed6f05.svg" alt="Reddit Logo"&gt;
          &lt;a href="https://www.reddit.com/r/webdev/comments/l36w9s/finally_understand_testing_and_fully_tested_my/" rel="noopener noreferrer"&gt;
            Finally understand testing and fully tested my React based cross browser extension with Jest!!! No better feeling then 100% code coverage.
          &lt;/a&gt;
        &lt;/h1&gt;
        &lt;div class="ltag__reddit--post-metadata"&gt;
          &lt;span&gt;Jan 23 '21&lt;/span&gt;
          &lt;span&gt;Author: lbragile_dev&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__reddit--body"&gt;
    &lt;p&gt;
        &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CfIcDQq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://a.thumbs.redditmedia.com/kmP5k6DhBaGtw5rq-hMIKZpic_Pppuy9Y7wgQ3JzmB0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CfIcDQq4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://a.thumbs.redditmedia.com/kmP5k6DhBaGtw5rq-hMIKZpic_Pppuy9Y7wgQ3JzmB0.jpg" alt="Finally understand testing and fully tested my React based cross browser extension with Jest!!! No better feeling then 100% code coverage."&gt;&lt;/a&gt;
    &lt;/p&gt;
  &lt;/div&gt;
  &lt;div class="ltag__reddit--btn--container"&gt;
    
      &lt;a href="https://www.reddit.com/r/webdev/comments/l36w9s/finally_understand_testing_and_fully_tested_my/" rel="noopener noreferrer"&gt;See Full Post&lt;/a&gt;
    
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  2. Linting - Static Testing 📐 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;You must have heard about &lt;a href="https://en.wikipedia.org/wiki/Lint_(software)"&gt;linting&lt;/a&gt; by now and how amazing it is, but never wanted to delve into it since it sounds too complicated for little to no benefits. I felt exactly the same way until I started using it - and let me tell you linting is beyond &lt;em&gt;amazing&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Source Code Linting &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Add &lt;a href="https://eslint.org/"&gt;ESLint&lt;/a&gt; to your project (even if you plan to use TypeScript). Imagine writing a very long essay/thesis in a Word document without grammar highlighting - do you think you will be flawless? Isn't it nice to be warned of any inaccuracies/errors you made right away?&lt;/p&gt;

&lt;p&gt;That's exactly ESLint's purpose inside your VSCode IDE (assuming everything is setup right). You can configure it to follow specific rules according to your liking. So far, this fixed &lt;strong&gt;a lot&lt;/strong&gt; of issues in my code - from small to &lt;em&gt;large&lt;/em&gt; - and even allowed me to learn new Javascript concepts. &lt;/p&gt;

&lt;p&gt;For example, I learned that &lt;code&gt;const&lt;/code&gt; means constant &lt;u&gt;reference&lt;/u&gt; rather than simply &lt;u&gt;value&lt;/u&gt;, so you could actually have a &lt;code&gt;const&lt;/code&gt; array whose elements can be changed, added or removed. The same is not true for a &lt;code&gt;const&lt;/code&gt; variable. That is,&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;arr&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;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&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="c1"&gt;// valid&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// [1]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;val&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="c1"&gt;// error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an example of what ESLint looks like:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jHWlph2I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mry7x7mm03s5i4okao6a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jHWlph2I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mry7x7mm03s5i4okao6a.png" alt="ESLint in VS Code" width="465" height="127"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;As you can see, the instructions are very clear and you are even provided with quick actions from VSCode which can be accessed with &lt;code&gt;ctrl + .&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Style Sheet Linting &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Now that you have ESLint working, you should consider &lt;a href="https://stylelint.io/user-guide/get-started"&gt;StyleLint&lt;/a&gt; for your styling files (CSS, SASS, LESS, etc.). &lt;/p&gt;

&lt;p&gt;This helps reduce duplicates that are scattered across your many files on large projects. StyleLint also enforces best standards such as the following:&lt;/p&gt;
&lt;h6&gt;
  
  
  Spacing Errors
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i_jDEhQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v33t5aeqffp3keoyonml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i_jDEhQq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v33t5aeqffp3keoyonml.png" alt="Stylelint Spacing Errors in VS Code" width="880" height="187"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  Unit Errors
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mfTiMfrA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2shex6hamexn5ja2lra5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mfTiMfrA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2shex6hamexn5ja2lra5.png" alt="Stylelint Unit Errors in VS Code" width="607" height="257"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  Duplicates
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NLm_GYcf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4ahd3957lc1ta2b9kv2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NLm_GYcf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x4ahd3957lc1ta2b9kv2.png" alt="Stylelint Duplicates in VS Code" width="880" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, StyleLint detects when you forget to add blank lines between blocks of styles and/or if you have an extra space in a block comment like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* &amp;lt;- space
 * comment
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  TabMerger Linting
&lt;/h4&gt;

&lt;p&gt;TabMerger uses both linting types dynamically (through the use of IDE extensions: &lt;code&gt;ESLint&lt;/code&gt; &amp;amp; &lt;code&gt;stylelint&lt;/code&gt;) and manually:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run lint&lt;/code&gt; → ESLint&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run lint:style&lt;/code&gt; → StyleLint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Manually linting will produce a report in the command line that will outline all the errors across all files so that you can quickly find them (rather than opening each file one by one). Here is an example: &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---PWmnm9w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nom75u5lxzkcl5bsndr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---PWmnm9w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nom75u5lxzkcl5bsndr.png" alt="Manual Style Lint in TabMerger" width="787" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; a clean run will produce no output.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. TypeScript ✍ &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;With both of these linters, you will be happy to know that &lt;a href="https://www.typescriptlang.org/"&gt;TypeScript&lt;/a&gt; (TS) also offers "linting" in the form of &lt;u&gt;type control&lt;/u&gt;. &lt;/p&gt;

&lt;p&gt;This is &lt;strong&gt;super&lt;/strong&gt; useful when working on any project as you can quickly hover over &lt;strong&gt;anything&lt;/strong&gt; (function, variable, etc.) to get more type specific information in your IDE. TypeScript is also very well integrated with VS Code.&lt;/p&gt;

&lt;p&gt;Typing information can be seen by hovering:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kcFjxL5M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jo6m6kycycewpz9fqrre.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kcFjxL5M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jo6m6kycycewpz9fqrre.png" alt="TypeScript Type Information" width="426" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is how TypeScript errors look like:&lt;/p&gt;
&lt;h6&gt;
  
  
  Error
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P2GvBu6F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3fmns9pqqhytzkqyae1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P2GvBu6F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n3fmns9pqqhytzkqyae1.png" alt="TypeScript Error Demonstration" width="880" height="258"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h6&gt;
  
  
  No Error
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CqkH9JhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92ln6hfi8aqvi0codtg0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CqkH9JhN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/92ln6hfi8aqvi0codtg0.png" alt="TypeScript Without Error Demonstration" width="880" height="181"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A good point about TypeScript is that you can slowly/incrementally modify your JavaScript files in existing projects and everything will still work fine - since TS files get compiled to JS. Check out &lt;a href="https://github.com/lbragile/TabMerger"&gt;TabMerger's repository&lt;/a&gt; to see how I structured my project to incorporate and successfully run with TypeScript.&lt;/p&gt;

&lt;p&gt;I am still in the process of learning TypeScript and in TabMerger I currently have around 100 places where I am not sure how to "type" properly - so I am by no means a master with TypeScript - but ever since I started using it, I haven't looked back. &lt;/p&gt;

&lt;p&gt;So far, I've refactored pretty much all my old projects to include TypeScript in one way or another. The typing information it provides and just the process of migrating to TypeScript is very rewarding and useful! &lt;/p&gt;

&lt;p&gt;It will take longer to get used to TypeScript than linting but it should not take too long before you start seeing how useful TS is.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Module Aliasing 💥 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Tired of looking up your directory tree to know the relative path of your &lt;code&gt;import&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This can certainly slow down your workflow and is not very practical when you consider the fact that users/contributors do not want to look up your structure just to use your module. It is very common for npm/yarn packages to have their module paths aliased to simpler names that are mapped to the correct paths.&lt;/p&gt;

&lt;p&gt;To do this in TS, you can add the &lt;code&gt;baseURL&lt;/code&gt; and &lt;code&gt;paths&lt;/code&gt; options to your TS configuration file. If done right, this allows you to &lt;code&gt;import { A } from @A/A&lt;/code&gt; instead of &lt;code&gt;import { A } from ../components/A/A&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Example from one of TabMerger's files:&lt;/p&gt;
&lt;h6&gt;
  
  
  Without Aliasing
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uB6m9v-1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oyvbylg0ojqjjn5plj0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uB6m9v-1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oyvbylg0ojqjjn5plj0d.png" alt="Without Module Aliasing in TabMerger" width="658" height="117"&gt;&lt;/a&gt; &lt;/p&gt;
&lt;h6&gt;
  
  
  With Aliasing
&lt;/h6&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--deNwam8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vgcpuqy5pg7uhv8ahme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--deNwam8S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0vgcpuqy5pg7uhv8ahme.png" alt="With Module Aliasing in TabMerger" width="647" height="123"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Unfortunately, React's build scripts prevent the &lt;code&gt;paths&lt;/code&gt; option in the &lt;code&gt;tsconfig.json&lt;/code&gt;, so a work around is needed to get this working properly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;npm i -D react-app-rewired&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;config-overrides.js&lt;/code&gt; to root (see TabMerger's &lt;a href="https://github.com/lbragile/TabMerger/blob/master/config-overrides.js"&gt;file&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;alias&lt;/code&gt; object matches your aliases as shown in the file from the previous step&lt;/li&gt;
&lt;li&gt;change &lt;code&gt;start: react-scripts start&lt;/code&gt; to &lt;code&gt;start: react-app-rewired start&lt;/code&gt; and the same for the build script (see TabMerger's &lt;a href="https://github.com/lbragile/TabMerger/blob/master/package.json#L51"&gt;file&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You will also need to &lt;a href="https://github.com/lbragile/TabMerger/blob/master/config/jest.config.js#L14"&gt;adjust&lt;/a&gt; your &lt;code&gt;jest.config.js&lt;/code&gt; by adding the alias' and their corresponding true paths to the &lt;code&gt;moduleNameMapper&lt;/code&gt; property. &lt;/p&gt;

&lt;p&gt;Note that you can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/n"&gt;RegExp variables&lt;/a&gt; to shorten these key/value pairs.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Documentation 📚 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;By now, you might have noticed that the functions I posted in some of the above images have a specific comment syntax. Something like:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LXY5buoT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lg93pihdp2avzz4f14i7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LXY5buoT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lg93pihdp2avzz4f14i7.png" alt="TabMerger's Documentation Comments" width="819" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is done on purpose to generate good looking documentation as seen &lt;a href="https://lbragile.github.io/TabMerger/modules/app_app_functions.html"&gt;here&lt;/a&gt;. The main modules which generate these documentation reports are &lt;a href="https://jsdoc.app/"&gt;jsDoc&lt;/a&gt; (Javascript) and &lt;a href="https://typedoc.org/"&gt;typeDoc&lt;/a&gt; (TypeScript). &lt;/p&gt;

&lt;p&gt;Commenting your code like this will make it much easier to follow for anyone who visits it for the first time. It might also allow you to remember that hard to understand part in your code. The added bonus of using such comments for documentation is that it makes the transition from JS to TS much smoother as you can "infer" types from the comments using VS Code to automatically type your function arguments and return values.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://lbragile.github.io/TabMerger/"&gt;see&lt;/a&gt; that only specific modules are shown on the documentation's main page. This can be controlled by a &lt;a href="https://github.com/lbragile/TabMerger/blob/master/config/typedoc.config.json"&gt;configuration file&lt;/a&gt; and by adding the following to the top of your respective files:&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="cm"&gt;/**
 * @module MODULE_NAME
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  TabMerger Documentation Generation
&lt;/h4&gt;

&lt;p&gt;In TabMerger, you can use the following commands to generate documentation reports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run jsdoc&lt;/code&gt; (JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm run typedoc&lt;/code&gt; (TypeScript)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;I hope my suggestions are useful to some of you. I urge you to take action and add these items to your project as soon as possible. &lt;/p&gt;

&lt;p&gt;I can almost guarantee that you will instantly be more excited to work on your project(s) (granted everything works as expected). Worst thing that can happen is you cannot get one of the items to work properly or just don't find it that useful. In that case, you can simply revert back to the good old days 😊.&lt;/p&gt;

&lt;p&gt;I am actually in the process of looking for work so have a "lot" of spare time to help anyone who is confused or stuck on any of these items.&lt;/p&gt;

&lt;p&gt;Cheers 🥂&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
