<?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: Patrik</title>
    <description>The latest articles on DEV Community by Patrik (@nibi).</description>
    <link>https://dev.to/nibi</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%2F144628%2F52e2d782-7669-44f8-8d95-4f2631bf3b70.jpg</url>
      <title>DEV Community: Patrik</title>
      <link>https://dev.to/nibi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nibi"/>
    <language>en</language>
    <item>
      <title>Voice User Interface in tiny react project</title>
      <dc:creator>Patrik</dc:creator>
      <pubDate>Sat, 23 Sep 2023 19:25:22 +0000</pubDate>
      <link>https://dev.to/nibi/voice-user-interface-in-tiny-react-project-3jja</link>
      <guid>https://dev.to/nibi/voice-user-interface-in-tiny-react-project-3jja</guid>
      <description>&lt;p&gt;Sometimes during exercise, I want to track how long I’m able to hold the plank position or a certain stretch. However this situation makes it awkward to use my phone and this inspired me to explore Web Speech API and make voice controlled stopwatch in react. In this article, I’m focusing on adding voice commands to the existing react tiny project. You can follow along the &lt;a href="https://github.com/patrikniebur/voice-controlled-timer/" rel="noopener noreferrer"&gt;codebase on github&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Speech Recognition in browsers
&lt;/h3&gt;

&lt;p&gt;The SpeechRecognition interface is part of the experimental Web Speech API and it makes it easy to implement voice user interface (VUI) in browsers. Official browser support looks great, however in practice, it is less optimistic.  According to my tests with multiple browsers, it has been working reliably only in Chrome and Safari. &lt;/p&gt;

&lt;h3&gt;
  
  
  Tiny project with voice user interface
&lt;/h3&gt;

&lt;p&gt;The only thing I want to render is the &lt;code&gt;Timer&lt;/code&gt; component inside the &lt;code&gt;App&lt;/code&gt; where I will add controls using voice commands. This is what the initial structure looks like:&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVoiceCommands&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&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="c1"&gt;// Placeholder for adding voice commands&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useVoiceCommands&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="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Note the placeholder hook &lt;code&gt;useVoiceCommands&lt;/code&gt; which will be used to add - you guessed it, voice command functionality.&lt;/p&gt;

&lt;p&gt;Lucky for me there is already a package available for speech recognition in react called &lt;a href="https://github.com/JamesBrill/react-speech-recognition" rel="noopener noreferrer"&gt;react-speech-recognition&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This package offers an option to add 3rd party recognition service as a polyfill for full cross-browser support. For this project, I'm happy with Chrome and Safari support so I will not investigate this option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom &lt;code&gt;useVoiceCommands&lt;/code&gt; hook
&lt;/h3&gt;

&lt;p&gt;Let's start building our &lt;code&gt;useVoiceCommands&lt;/code&gt; hook by importing the building blocks from &lt;code&gt;react-speech-recognition&lt;/code&gt;.&lt;/p&gt;

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

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSpeechRecognition&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-speech-recognition&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;react-speech-recognition&lt;/code&gt; package exports &lt;code&gt;useSpeechRecognition&lt;/code&gt; hook to read recognized speech in our component, and &lt;code&gt;SpeechRecognition&lt;/code&gt; object to be used to start and stop the recognition service. I want to start the speech recognition when app renders so I will call it inside React.useEffect hook with an empty dependency array, including &lt;code&gt;abortListening&lt;/code&gt; method in the clean up function.&lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useVoiceCommands&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&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;// Timeout fixes the error during live-refresh in development when listening&lt;/span&gt;
            &lt;span class="c1"&gt;// attempted to start before it has been aborted in a cleanup&lt;/span&gt;
        &lt;span class="nf"&gt;setTimeout&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="nx"&gt;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startListening&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;continuous&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
          &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Stop listening when component is unmounted.&lt;/span&gt;
        &lt;span class="k"&gt;return &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;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abortListening&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let’s define the commands for the stopwatch:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;start&lt;/code&gt; - start the timer&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pause&lt;/code&gt; - pause at the current time&lt;/p&gt;

&lt;p&gt;&lt;code&gt;stop&lt;/code&gt; / &lt;code&gt;reset&lt;/code&gt; - stop and set the stopwatch back to zeroes. &lt;/p&gt;

&lt;p&gt;These words will have to be recognized and used in the &lt;code&gt;App&lt;/code&gt; to interact with the timer. At the moment our app starts listening to microphone input but we don’t read the output yet so let’s do this next. Add the &lt;code&gt;useSpeechRecognition&lt;/code&gt; hook imported from react-speech-recognition package into the custom &lt;code&gt;useVoiceCommands&lt;/code&gt; hook.  Its output is an object containing properties indicating browser support and transcribe details.  &lt;/p&gt;

&lt;p&gt;If you want to learn more, I recommend consulting the &lt;a href="https://github.com/JamesBrill/react-speech-recognition/blob/master/docs/API.md#useSpeechRecognition" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this use case, we will want to trigger state change when specific words are recognized so we can ignore the transcript properties and disable state update in &lt;code&gt;useSpeechRecognition&lt;/code&gt; hook by adding a configuration object into its call with transcribing set to false:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useSpeechRecognition({ transcribing: false })&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Everything indicates browser support speech recognition in all major browsers (Firefox behind flag) but at the time of writing, it is operating correctly only in Chrome and Safari&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;React speech recognition has an excellent feature for exactly what we need. It is called “commands” and we can use it to define specific words we want to listen for, with fuzzy match and callback to be triggered upon recognition. In the command array, we put all the commands we want to be recognized, and as a callback, we will set the state value of the &lt;code&gt;lastCommand&lt;/code&gt;. This is what the final version of the custom &lt;code&gt;useVoiceCommands&lt;/code&gt; hook looks like:  &lt;/p&gt;

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

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useVoiceCommands&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lastCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLastCommand&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useSpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;transcribing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;commands&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="na"&gt;command&lt;/span&gt;&lt;span class="p"&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;start&lt;/span&gt;&lt;span class="dl"&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;stop&lt;/span&gt;&lt;span class="dl"&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;reset&lt;/span&gt;&lt;span class="dl"&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;pause&lt;/span&gt;&lt;span class="dl"&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;pose&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="cm"&gt;/* pose - catches misunderstood pause */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="na"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setLastCommand&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;matchInterim&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;isFuzzyMatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&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;// Timeout fixes error during development when listenning attempted to start&lt;/span&gt;
    &lt;span class="c1"&gt;// before it has been aborted in a cleanup&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&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="nx"&gt;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startListening&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;continuous&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;SpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abortListening&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lastCommand&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;h3&gt;
  
  
  Using the voice commands in app
&lt;/h3&gt;

&lt;p&gt;By using the custom hook for getting the voice command we are keeping the body of the App function component concise and clean. This component will make use of &lt;code&gt;lastCommand&lt;/code&gt; in the following ways.  &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;lastCommand&lt;/code&gt; state variable will be used to conditionally set the &lt;code&gt;run&lt;/code&gt; prop of timer. If its value is &lt;code&gt;start&lt;/code&gt; - the &lt;code&gt;run&lt;/code&gt; will be true which starts the timer. When we say pause, &lt;code&gt;run&lt;/code&gt; prop will change to false and timer will pause displaying the time that has passed since start.&lt;br&gt;
Same thing actually happens when you say "stop" or "reset", but for these commands we will set the time back to zeros.&lt;/p&gt;

&lt;p&gt;In order to reset the Timer back to zero, we will force the component to re-initialize by adding the &lt;code&gt;key&lt;/code&gt; prop and change its value when either “stop” or “reset” commands are recognized. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;resetKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setResetKey&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastCommand&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useVoiceCommands&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reset&lt;/span&gt;&lt;span class="dl"&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;stop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastCommand&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setResetKey&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;lastCommand&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Timer&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;resetKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;lastCommand&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Changing the value of the &lt;code&gt;key&lt;/code&gt; prop will force react to unmount the existing instance and mount new one which coincides with our goal to reset the timer.&lt;/p&gt;

&lt;p&gt;Now the app utilizes all of the commands we set to use for this tiny project. Remember when you test this, that indicated browser support for SpeechRecognition API is not reliable and it is best to run this project in Chrome. At the moment react-speech-recognition does not reveal the error state (I have submitted &lt;a href="https://github.com/JamesBrill/react-speech-recognition/pull/189" rel="noopener noreferrer"&gt;PR for this&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqdo5qhraros87ndon04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgqdo5qhraros87ndon04.png" alt="Screenshot of voice controlled stopwatch demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To help you check the support for your browser I have added an error listener on the &lt;a href="https://patrikniebur.github.io/voice-controlled-timer/" rel="noopener noreferrer"&gt;demo&lt;/a&gt; of this project along with the display of debugging information. After you enable microphone permissions and start talking it will either display recognized commands or the error code on the screen, so you don’t end up talking to your computer for no reason 🙂&lt;/p&gt;

&lt;p&gt;Hopefully, this tiny project walkthrough gave you an idea of how to start adding a voice user interface to your projects. Let me know in the comments all your questions or your feedback on this walkthrough or what you are going to use the speech recognition for. If you see something that could be done better feel free to submit a PR and I will be happy to post an update.&lt;/p&gt;

&lt;h3&gt;
  
  
  Useful resources for your projects using speech recognition:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition" rel="noopener noreferrer"&gt;MDN SpeechRecognition&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/JamesBrill/react-speech-recognition" rel="noopener noreferrer"&gt;react-speech-recognition&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://caniuse.com/?search=SpeechRecognition" rel="noopener noreferrer"&gt;Can I use SpeechRecognition&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/patrikniebur/voice-controlled-timer/" rel="noopener noreferrer"&gt;This project on github&lt;/a&gt; and &lt;a href="https://patrikniebur.github.io/voice-controlled-timer/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@jasonrosewell?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Jason Rosewell&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/ASKeuOZqhYU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>speechrecognition</category>
      <category>beginners</category>
      <category>react</category>
      <category>learning</category>
    </item>
    <item>
      <title>How to use apollo client cache for local state management</title>
      <dc:creator>Patrik</dc:creator>
      <pubDate>Sat, 17 Dec 2022 20:25:27 +0000</pubDate>
      <link>https://dev.to/nibi/using-apollo-client-cache-for-local-state-3peb</link>
      <guid>https://dev.to/nibi/using-apollo-client-cache-for-local-state-3peb</guid>
      <description>&lt;p&gt;Apollo client is popular tool of choice for communicating with graphQL API. With its build-in cache capability it is also a good choice for state management. Let's take a look at how to perform CRUD actions on an example to-do app, using apollo client cache for local state management.  &lt;/p&gt;

&lt;p&gt;This walkthrough uses TypeScript, react (18.2.0) and &lt;a class="mentioned-user" href="https://dev.to/apollo"&gt;@apollo&lt;/a&gt;/client (3.7.1). User interface is purposefully basic to allow us to focus on interacting with the state. It is composed of four parts:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AddTodo&lt;/code&gt; a component with a text input element for adding to-dos
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TodoList&lt;/code&gt; which displays all &lt;code&gt;TodoItem&lt;/code&gt;s that will toggle completed state on click
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ClearCompleted&lt;/code&gt; button that removes completed to-dos from cache
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Complete code is available on &lt;a href="https://codesandbox.io/s/mystifying-hodgkin-ybve5u" rel="noopener noreferrer"&gt;codesandbox&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial config
&lt;/h2&gt;

&lt;p&gt;Let's start by defining our apollo client using &lt;code&gt;InMemoryCache&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client.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;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;InMemoryCache&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;@apollo/client&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryCache&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;And make it available to react via &lt;code&gt;ApolloProvider&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.tsx&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;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ApolloProvider&lt;/span&gt; &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Todo list&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddTodo&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TodoList&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ClearCompleted&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ApolloProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup initial state
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useQuery&lt;/code&gt; has default fetch policy set to &lt;code&gt;cache-first&lt;/code&gt;. This policy means that apollo client will check the cache before attempting to send a network request to fetch the data. We want to use only a local state (in a cache) without using network, so initial state will need to be written into the cache before client requests the data.   &lt;/p&gt;

&lt;p&gt;Going back to the &lt;code&gt;client.ts&lt;/code&gt; file, write the initial data before the react app is mounted.&lt;br&gt;
Call &lt;code&gt;writeQuery&lt;/code&gt; on apollo client instance, defining the query to write into and the value of initial data.&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;// client.ts&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
    query {
      allTodos
    }
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// initially an empty array&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;allTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Structure of the &lt;code&gt;data&lt;/code&gt; property must conform the structure of the query. In the code above, query defining allTodos field expects &lt;code&gt;data&lt;/code&gt; object containing property with the same name.   &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the data property does not contain all the fields defined in the query, apollo will log the error in a console&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This call creates canonical field &lt;code&gt;allTodos&lt;/code&gt; on the root &lt;code&gt;Query&lt;/code&gt; type. We can confirm this by opening &lt;a href="https://github.com/apollographql/apollo-client-devtools" rel="noopener noreferrer"&gt;apollo devtools&lt;/a&gt; and viewing the 'cache' tab.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk32gcvtszov7113s94uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk32gcvtszov7113s94uy.png" alt="Apollo devtool showing allTodos field having a value of empty array"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Type policies
&lt;/h2&gt;

&lt;p&gt;By default all query writes for &lt;code&gt;allTodos&lt;/code&gt; will replace its content. Our goal is to use &lt;code&gt;allTodos&lt;/code&gt; field for overwriting all stored to-dos and for adding individual to-dos into the cached array. The default write behaviour can be changed by defining the type policy on the root &lt;code&gt;Query&lt;/code&gt; type, specifying &lt;code&gt;allTodos&lt;/code&gt; field with the object containing the &lt;code&gt;merge&lt;/code&gt; method.&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;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InMemoryCache&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;typePolicies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// type we want to define the policy for&lt;/span&gt;
      &lt;span class="na"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// field for which we want to customize the behaviour&lt;/span&gt;
          &lt;span class="na"&gt;allTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// how we want to merge existing data with the incoming&lt;/span&gt;
            &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;

              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Value returned by &lt;code&gt;merge&lt;/code&gt; will be stored as a new value for &lt;code&gt;allTodos&lt;/code&gt;. It will be invoked for all the query writes into &lt;code&gt;allTodos&lt;/code&gt; field, even for the initial state write.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;merge&lt;/code&gt; defined above returns the &lt;code&gt;incoming&lt;/code&gt; value as-is if it is array, overwriting previously cached value which was our first goal for this field. In any other case &lt;code&gt;incoming&lt;/code&gt; will be returned as a last item in an array of existing data.   &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Apollo suggests that merge function is 'pure' - not mutating the input arguments since internally the objects are passed as references and mutating them can cause unexpected side-effects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Adding a to-do item
&lt;/h2&gt;

&lt;p&gt;Adding a to-do is done similarly to setting the initial state. This time the query will define all the fields that will construct the to-do object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AddTodo.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todoQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query {
    allTodos {
      __typename
      id
      title
      completed
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Including the &lt;code&gt;__typename&lt;/code&gt; is necessary for data normalization in apollo. If you are not familiar with this concept I recommend learning more about it from &lt;a href="https://www.apollographql.com/blog/apollo-client/caching/demystifying-cache-normalization/" rel="noopener noreferrer"&gt;this article&lt;/a&gt;. In short - it allows deduplication of data, enables recognition of correct type for using type policies, updating objects using &lt;code&gt;writeFragment&lt;/code&gt; and other useful stuff.  &lt;/p&gt;

&lt;p&gt;After our todoQuery is defined we will use it in a helper function which is invoked when you press "Add Todo"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AddTodo.tsx&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todoQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;allTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As before it is necessary for the structure of the &lt;code&gt;data&lt;/code&gt; to match the structure of the query. &lt;code&gt;__typename&lt;/code&gt; is set to &lt;code&gt;Todo&lt;/code&gt; which will be important later when we get to updating the &lt;code&gt;completed&lt;/code&gt; state.&lt;br&gt;&lt;br&gt;
You have probably noticed that for value of &lt;code&gt;allTodos&lt;/code&gt; we have set a single object and not an array as before when writing the initial state. This is when the &lt;code&gt;merge&lt;/code&gt; field policy on &lt;code&gt;allTodos&lt;/code&gt; that was defined earlier comes in useful. Let's review it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client.tsx&lt;/span&gt;
    &lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;incoming&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;incoming&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;&lt;code&gt;existing&lt;/code&gt; is always the current value of the field and &lt;code&gt;incoming&lt;/code&gt; will be set to the value of the to-do object we want to add using &lt;code&gt;writeQuery&lt;/code&gt;. &lt;code&gt;incoming&lt;/code&gt; fails the &lt;code&gt;isArray&lt;/code&gt; test and is appended to the array of existing to-dos - in simpler terms: new to-do is added to the end of the list of existing to-dos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query To-dos
&lt;/h2&gt;

&lt;p&gt;If you have used react apollo client, the following code will be familiar. Requesting data from cache is the same as requesting them from graphql server. First you define the query and then use it as a first argument in useQuery hook&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TodoList.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allTodosQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query getAllTodos {
    allTodos {
      id
      title
      completed
    }
  }
`&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;function&lt;/span&gt; &lt;span class="nf"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allTodosQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// data: { allTodos: [{...}] }&lt;/span&gt;
  &lt;span class="cm"&gt;/* rest of the component body */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default fetch policy for &lt;code&gt;useQuery&lt;/code&gt; (&lt;code&gt;cache-first&lt;/code&gt;) means that cache will be queried for data and it returns the empty array that was setup as initial state.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If cache had no data for this query it will attempt to request if from the server. We can prevent this behaviour by &lt;a href="https://www.apollographql.com/docs/react/data/queries/#setting-a-fetch-policy" rel="noopener noreferrer"&gt;changing the fetchPolicy&lt;/a&gt; to &lt;code&gt;cache-only&lt;/code&gt;.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally to getting the data from cache, &lt;code&gt;useQuery&lt;/code&gt; will set up subscription on the cached data. This means that whenever the value of &lt;code&gt;allTodos&lt;/code&gt; is changed, component will be re-rendered with the latest cached value.   &lt;/p&gt;

&lt;p&gt;Now that &lt;code&gt;TodoList&lt;/code&gt; can get the data from cache it can render individual items as &lt;code&gt;TodoItem&lt;/code&gt; components so now we can see the them in the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating to-do
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;TodoItem&lt;/code&gt; takes to-do object as a prop and display its data. In this component we will want to add a functionality of toggling the &lt;code&gt;completed&lt;/code&gt; state when user clicks on it. We will use &lt;code&gt;writeFragment&lt;/code&gt; method to-do the update, which will require a fragment on a type that we want to edit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TodoItem.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  fragment ToggleComplete on Todo {
    completed
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This code creates a fragment on &lt;code&gt;Todo&lt;/code&gt; type, which matches the value of &lt;code&gt;__typename&lt;/code&gt; we have used when adding to-do.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In a fragment we need to specify the properties that we will want to change, in this case it is just &lt;code&gt;completed&lt;/code&gt; field. As a next step we will define &lt;code&gt;toggleComplete&lt;/code&gt; helper that we will call when user clicks on the to-do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TodoItem.tsx&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toggleComplete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFragment&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;identify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;__typename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Todo&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;todo&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&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;Notice the &lt;code&gt;client.cache.identify({ __typename: "Todo", ...todo })&lt;/code&gt; utility used that reads the object &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;__typename&lt;/code&gt;. The output will look something like: &lt;code&gt;Todo:123&lt;/code&gt;. This is the 'ref' of the normalized object stored in a cache which tells the cache what object will be updated.    &lt;/p&gt;

&lt;p&gt;Now when you click the to-do item and &lt;code&gt;toggleComplete&lt;/code&gt; is called, it will update the &lt;code&gt;completed&lt;/code&gt; property of the to-do object in cache. Cache change in turn triggers subscription set by &lt;code&gt;useQuery&lt;/code&gt; in &lt;code&gt;TodoList&lt;/code&gt; causing it to re-render with the new data that are being passed as a props into &lt;code&gt;TodoItem&lt;/code&gt;. In the UI you will see to-do item that was clicked get crossed out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting Todos
&lt;/h2&gt;

&lt;p&gt;To complete the CRUD implementation we will add delete functionality of the completed to-dos.  &lt;/p&gt;

&lt;p&gt;In order to delete completed to-dos we need to get the list of to-dos, filter out the completed ones and write the new list back. All of this can be done with &lt;code&gt;updateQuery&lt;/code&gt; method, so let's start by defining the query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ClearCompleted.tsx&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryTodos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  query Todos {
    allTodos {
      id
      completed
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The update needs &lt;code&gt;id&lt;/code&gt; so cache can recognize what to-dos are written back and &lt;code&gt;completed&lt;/code&gt; to let us filter out completed to-dos. &lt;/p&gt;

&lt;p&gt;Update helper will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clearCompletedTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApolloClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;queryTodos&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&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="na"&gt;allTodos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allTodos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;completed&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;completed&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gc&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 time the function signature is a little different. In a first parameter query must be defined in an object and the second parameter is update function. It receives &lt;code&gt;data&lt;/code&gt; from the query in a shape as requested and it is expected to return the data we would like to have stored on that query. Code above returns object with &lt;code&gt;allTodos&lt;/code&gt; property (again matching the shape of the query) with filtered to-dos. This update, same as previous writes, will invoke the &lt;code&gt;merge&lt;/code&gt; field policy and because we are writing an array it replaces all of its content.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're wondering how are the titles of the to-dos preserved when it is not requested in the update query, it is because of the data normalization that happens internally. The to-do objects that are written back to the query contain &lt;code&gt;id&lt;/code&gt; and &lt;code&gt;__typename&lt;/code&gt; - which is requested implicitly, and those two properties are enough to match the object to normalized version stored in cache.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the update query, &lt;a href="https://www.apollographql.com/docs/react/caching/garbage-collection/" rel="noopener noreferrer"&gt;cache garbage collection&lt;/a&gt; is invoked to clean up now unreachable normalized objects.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This currently doesn't work as expected so I have logged this &lt;a href="https://github.com/apollographql/apollo-client/issues/10307" rel="noopener noreferrer"&gt;issue&lt;/a&gt; in apollo client repo.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;We've covered the cache policies and when they are invoked, setting up the initial state, writing objects into an array and the importance of &lt;code&gt;__typename&lt;/code&gt;, querying the data from cache, using fragment to update the normalized object and finally deleting the completed to-dos.  &lt;/p&gt;

&lt;p&gt;Thank you for reading this article, I hope you found it helpful. When you have a minute to spare I would love to hear your feedback on the information in the article, whether you have learned something new or any suggestions how to improve in my future writing.&lt;/p&gt;

</description>
      <category>graphql</category>
      <category>apollo</category>
      <category>react</category>
      <category>state</category>
    </item>
    <item>
      <title>Failing as a team leader</title>
      <dc:creator>Patrik</dc:creator>
      <pubDate>Thu, 30 Sep 2021 21:32:44 +0000</pubDate>
      <link>https://dev.to/nibi/failing-as-a-team-leader-19hn</link>
      <guid>https://dev.to/nibi/failing-as-a-team-leader-19hn</guid>
      <description>&lt;p&gt;Becoming a good leader is a journey that takes time and patience. There is plenty of articles out there about how to become one but what about looking at common mistakes most new leaders make? Even if you strive to be the best you will not be able to avoid some failures on your journey. Here I'm sharing mistakes I made in my first leadership role so that you can learn from them too.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;I've been building websites for some time (I was learning doing layouts with tables to give you an idea) but only had 4 years of professional experience before my promotion. I knew the team and the product we worked on quite well. After being in the company for a year I have confidently accepted promotion to a team leader role. I knew it will be exciting experience, yet I was not prepared for the amount of mistakes I was going to make along the way.   &lt;/p&gt;

&lt;h2&gt;
  
  
  You don't become a leader by getting promoted!
&lt;/h2&gt;

&lt;p&gt;This is not a breakthrough realization but many people including me is oblivious to the fact until it is verbalized to them. After promotion I did not suddenly become smarter, more capable or gained superpowers. I knew this but it did not stop me to act like it. I was trying to have an answer to every question and come up with solution to every problem which was not only overwhelming but also became frustrating when those answers and made up solutions turned out to be incorrect. Lot of bad decisions were made as I was trying to cover up that I do not have the correct answers.&lt;/p&gt;

&lt;p&gt;Looking back at this time I wish I had been more open about my lack of experience and used the opportunity to learn from other more experienced colleagues in leadership roles. It would allow me to learn more and be better in my role earlier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scared of delegation
&lt;/h2&gt;

&lt;p&gt;It is not an understatement when delegation is referred to as art. Same as art it needs understanding, personal touch, experience and lot of patience. While I was aware of the concept, I was actively trying to avoid it and take 'shortcuts' by using it as a last resort.  &lt;/p&gt;

&lt;p&gt;By doing this I was unknowingly taking away growth opportunities from other team members and passively indicating they don't have my trust. As a result they were losing confidence in their own work and grew doubtful of their own skills. And I instead of helping others to grow, was drowning under the workload that could be with appropriate support handled by anyone on the team.  &lt;/p&gt;

&lt;p&gt;Delegation is hard but it is effective tool to getting more done and enabling leaders to focus their time where it is most impactful. Getting it right takes practice and some experimentation but when done correctly it is invaluable skill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Giving too little freedom (micromanagement)
&lt;/h2&gt;

&lt;p&gt;When I actually delegated some work I resorted to the worst management approach of all - micromanaging. Incorrect assumptions about my role led me to believe that I have (or need to have) the best idea how things should be done. Again communicating lack of trust in my team members I repeatedly commanded every step of the project, what is the order of tasks and timing expectations without proper discussions.  &lt;/p&gt;

&lt;p&gt;As expected others became uncomfortable to share their own ideas and would suppress their opinions and creativity. In one of the meetings upon my request for ideas my colleague replied: "Why don't you just tell us the solution?". At first this lack of interest in cooperation made me angry, until I realized that this was the working relationship I have established.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Giving too much freedom
&lt;/h2&gt;

&lt;p&gt;In a poor attempt to improve on my micromanagement approach I chose the other end of the extreme scale. Instead of outlining the specific methods and solutions I've disengaged completely. After giving the loose project brief to my team, I would let everyone work on their own and only check-in for updates on a progress.  &lt;/p&gt;

&lt;p&gt;Initially this made everyone little bit happier (comparing to being micromanaged) but frustration was back towards the end of the project when we realized that work in progress not only goes far beyond deadline but does not fully meet project requirements. This was complete failure in my core responsibility - being a leader.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Lack of clear communication
&lt;/h2&gt;

&lt;p&gt;"Communicate, communicate, communicate!" were the words of wisdom from my colleague manager after another missed deadline due to miscommunication of the project brief. English is not my native language and same applies to most of my colleagues, however I do not think this was the problem. I simply assumed it is enough to say everything only once. Whatever the outcome was from the management meeting I only communicated directly relevant bits to the team without the rest of the details. No one else had any idea why do we work on this specific thing, how are the priorities set or how is the rest of the company affected by our work. I thought those details were not important to do the work so I kept them to myself. Same applied to communication to higher ups. I assumed all issues within my team or technology we were working with, were to be solved only by me and my team. Keeping the details to myself blocked my colleagues from making informed decisions. Lot of time was lost because incorrect priorities were set due to lack of communication and inherent failure to plan.&lt;br&gt;&lt;br&gt;
Best communication practices suggest to communicate the same message on multiple channels, reinforce by repetition, varying formulation and even ask the other party to confirm what was agreed to avoid misunderstanding. Communication is a critical tool that makes everything much harder when neglected.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Making changes without thinking
&lt;/h2&gt;

&lt;p&gt;It was clear to everyone that our existing workflow did not work well. Some of the bugs were not being reported through our bug tracker so when someone else came across the same bug it was unclear whether it is being worked on or not. When I investigated why is this happening, few people reported that system for reporting bugs is too complex so they preferred to report it directly to individual developers. To fix the complex bug tracker I have decided to change the software completely for next shiny new thing. This new system was  an improvement but because I have fixed the wrong problem it only made the whole situation worse. Now everyone complained that they don't know how to use the new bug tracker and other people wanted to go back to 'old ways' of doing things. I have not considered how impactful this change is and that it can be more difficult to adjust to.&lt;/p&gt;

&lt;p&gt;Before making such a changes evaluate what improvements it brings. Consider who will get affected, time investment required to make the change and cost of training. Then weigh these improvements against cost of implementation and training to make sure it will be worth to implement. Remember you don't need to (and probably should not) do this alone. Get input from other departments to help with final decision.&lt;/p&gt;

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

&lt;p&gt;Being a team leader for the first time is hard. Your safety net becomes much smaller and your decisions have bigger impact. If you are due to be promoted to leadership role do not get scared by my experience. Even though it might sound like I was only failing, there were lot of bright moments too.&lt;br&gt;
You probably will not make the same mistakes as I did but chances are you are gonna make some. And that's all fine, as you already know everybody makes mistakes. However it is important to learn from those mistakes as that is what moves you forward and makes you a better leader.&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>leadership</category>
      <category>failures</category>
    </item>
    <item>
      <title>Running for productivity</title>
      <dc:creator>Patrik</dc:creator>
      <pubDate>Sat, 28 Sep 2019 08:24:23 +0000</pubDate>
      <link>https://dev.to/nibi/running-for-productivity-2nde</link>
      <guid>https://dev.to/nibi/running-for-productivity-2nde</guid>
      <description>&lt;p&gt;As developers we often do not consider our physical fitness to be important for our work. While not essential, it makes a big impact on our performance and overall productivity. I started with running to improve my health but soon discovered other benefits.&lt;/p&gt;

&lt;h2&gt;
  
  
  One hour from start to finish
&lt;/h2&gt;

&lt;p&gt;Often it’s not easy to find yourself time for exercise. Knowing it only takes an hour of your time and you come back refreshed, makes it easier to fit in your schedule. Have 5 minutes for a warm up, 30 for a run, 10 post run stretch and you’re still left with 15 minutes for a shower.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality AFK time
&lt;/h2&gt;

&lt;p&gt;Sometimes the day just isn’t going well. Everything you touch breaks, nothing is making sense and your colleague Greg didn’t merge your PR because you used let instead of const. Getting away from keyboard and going for a run can help you focus better when you get back to a given task. You’ll see it with fresh eyes and mind, and it will be easier to focus again. This way you can get much more done than if you try to force yourself to do something you just don’t feel like doing.&lt;br&gt;
Many times I struggled to fix the bug or find a solution to ‘unsolvable’ problem for hours only to figure it out in my head during the exercise. When I’m running my brain is relaxed and my mind wanders to mysterious areas of humanity and life on the earth. If there’s something nagging me in the back of my mind, it naturally dissolves into clear solution or series of small steps to take, that will help me to solve the mystery. &lt;/p&gt;

&lt;h2&gt;
  
  
  Stress relief
&lt;/h2&gt;

&lt;p&gt;No matter how much pressure is on me it always goes away during the run. Tomorrow’s deadline for impossible task suddenly doesn’t matter at all when you are short of breath and fighting the thirst. Even after the exercise, stress levels can’t compete with load of endorphins in your body.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental health
&lt;/h2&gt;

&lt;p&gt;When you successfully beat stress and have regular ‘top-up’ of endorphins, you become a much happier person. In my case I became more optimistic. There’s no more long term stress that could have a bad effect on my mental health. And after you became happier, you’re more capable of spreading happiness too which will make you more likeable person. Of course there are other causes than stress that can make you depressed, but I found that running helps me cope a little better with anything life throws at me. &lt;/p&gt;

&lt;h2&gt;
  
  
  More energy than you expect!
&lt;/h2&gt;

&lt;p&gt;If you think morning run means you’ll be drained for the rest of the day, expect the opposite. Your body will wake up much more quickly and the energy will last for the whole day. It might get some time getting used to earlier wake-ups but benefits that come with it are worth it. Eventually exhaustion will catch you up in the evening when it’s time for bed, which guarantees better sleep. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final mile
&lt;/h2&gt;

&lt;p&gt;Lot of people say that running is boring and not for them. That’s fine I don't argue with them, I used to say the same. However when I started I found it enjoyable and now consider it part of my routine. There are many ways to keep yourself motivated. I recommend keeping track of your personal bests and try to beat them (shadow race your previous time using &lt;a href="https://www.endomondo.com/"&gt;endomondo&lt;/a&gt;), race your friends times, &lt;a href="https://zombiesrungame.com/"&gt;escape from zombies&lt;/a&gt; or if you like &lt;a href="http://thebeerrunner.org/"&gt;beer try doing both&lt;/a&gt;! &lt;br&gt;
If you are a runner too let me know in the comments other productivity benefits you have discovered.&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>health</category>
      <category>productivity</category>
      <category>weekendstory</category>
    </item>
    <item>
      <title>My running dev story</title>
      <dc:creator>Patrik</dc:creator>
      <pubDate>Sat, 21 Sep 2019 10:20:56 +0000</pubDate>
      <link>https://dev.to/nibi/my-running-dev-story-4a79</link>
      <guid>https://dev.to/nibi/my-running-dev-story-4a79</guid>
      <description>&lt;p&gt;It was just before 9am, I was walking up the stairs, catching my breath while cursing the elevator that was out of service for the third time that month. I wondered if I became unfit over the last couple of months. “No, it must be the steep stairs” inner voice tried to convince that fat angel in my head. Out of nowhere, noise of someone running up the stairs overlapped discussion in my head: “Morning! I’m in a hurry!”. It was our nearly 50 year old sales manager who just run up like a teenage gazelle, not paying attention to my fitness crisis. That made me realise it’s time to admit the harsh truth. Programming isn’t physical job and using stairs three times a month doesn’t count as a healthy workout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slow and short beginnings
&lt;/h2&gt;

&lt;p&gt;Coming home from a busy day at work it gets hard to convince myself to go out for a run. It helps when I consider that my mind can be tired but the rest of the body didn’t get the workout it needs. On my first run I felt little embarrassed how slow my pace is and despite that fact I was still sweating. Good thing about running is when people see you sweat they are more likely to think you’re on your fifteenth kilometer rather than recognising you just left the house. In fact I only started with as little as one kilometer. Next day I only ran one again and tried to compare my time to see if I got better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Help with motivation
&lt;/h2&gt;

&lt;p&gt;To keep myself motivated I started tracking my runs using endomondo app. I aimed to do at least three runs a week and on every run I tried to beat my previous time. After building up my distance to 5K I had to get better shoes for running. Until then my pair of old sneakers served the purpose. With new more suitable running shoes, things became easier. My pace has improved and I was able to increase the distance too. As a result of my regular runs my sleep got better because I was getting to bed tired, I felt more energised at work, got better at handling stress and became more positive overall. These side effects motivated my to continue running.&lt;/p&gt;

&lt;h2&gt;
  
  
  All the way to the marathon
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9r09u5ejldi1ngxvi8tv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9r09u5ejldi1ngxvi8tv.jpg" alt="Mo running event medals"&gt;&lt;/a&gt;&lt;em&gt;Photo: &lt;a href="http://www.mo-running.com" rel="noopener noreferrer"&gt;www.mo-running.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For my first running challenge I signed up for 10K race and supported the fight against cancer by participating. Fundraising runs are always a lot of fun, you can see people in funny costumes and if you’re lucky like me, you might get to race with sausage! &lt;/p&gt;

&lt;p&gt;After some time I wanted to step-up a challenge and committed to running a half-marathon. Training became more challenging but still enjoyable. The big day came as a reward after a couple of weeks of preparations. &lt;/p&gt;

&lt;p&gt;Completing a half-marathon was my first success as a runner which motivated me even more. I wanted to see if I could run a marathon. The idea of running 42K appeared crazy and impossible, which was a good incentive for me to sign up. About twelve weeks of training preceded my biggest sporting achievement. As well as my physical health I exercised my mental strength when waking up early for a run and resisting burgers for late night dinner. Finishing this long race gave me an amazing feeling of (pain and) accomplishment. Since then my confidence grew and now I know that any seemingly impossible task can be done with the right approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  It can help you too
&lt;/h2&gt;

&lt;p&gt;Without running, most likely I wouldn’t have enough mental energy to work on side projects, learn new technology, wouldn’t be able to cope with stress and as a result of that I wouldn’t progress in my career. You don’t even need to run marathon to enjoy the benefits and increase your productivity - which I’ll talk about more next time. &lt;br&gt;
If my story inspired you, grab a pair of sneakers and give running a go. There’s a big chance it will help you too.&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>motivation</category>
      <category>health</category>
      <category>weekendstory</category>
    </item>
  </channel>
</rss>
