DEV Community: official_dulin The latest articles on DEV Community by official_dulin (@mrdulin). https://dev.to/mrdulin 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%2F97713%2F579f0861-6211-4755-8091-877390251274.jpeg DEV Community: official_dulin https://dev.to/mrdulin en Why Material UI table component prop can violates w3c html element nesting rules? official_dulin Tue, 06 Aug 2024 08:01:41 +0000 https://dev.to/mrdulin/why-material-ui-table-component-prop-can-violates-w3c-html-element-nesting-rules-2n0m https://dev.to/mrdulin/why-material-ui-table-component-prop-can-violates-w3c-html-element-nesting-rules-2n0m <p>The <code>&lt;Table/&gt;</code> component has a <code>component</code> prop: </p> <blockquote> <p>The component used for the root node. Either a string to use a HTML element or a component.</p> </blockquote> <p>I set the <code>component</code> prop to <code>'p'</code> element for <code>&lt;Table/&gt;</code> and <code>&lt;TableHead /&gt;</code>, the table can render as normal.</p> <p>But, this will violates w3c html element nesting rules. See <a href="proxy.php?url=https://www.w3.org/TR/html401/struct/text.html#h-9.3.1" rel="noopener noreferrer">9.3.1 Paragraphs: the P element</a></p> <blockquote> <p>The <code>P</code> element represents a paragraph. It cannot contain block-level elements (including <code>P</code> itself).</p> </blockquote> <p>So, I'm worried that this will cause some potential problems. Can you explain is this safe to use?<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="k">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nf">CustomizedTables</span><span class="p">()</span> <span class="p">{</span> <span class="k">return </span><span class="p">(</span> <span class="o">&lt;</span><span class="nx">TableContainer</span> <span class="nx">component</span><span class="o">=</span><span class="p">{</span><span class="nx">Paper</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">Table</span> <span class="nx">sx</span><span class="o">=</span><span class="p">{{</span> <span class="na">minWidth</span><span class="p">:</span> <span class="mi">700</span> <span class="p">}}</span> <span class="nx">aria</span><span class="o">-</span><span class="nx">label</span><span class="o">=</span><span class="dl">"</span><span class="s2">customized table</span><span class="dl">"</span> <span class="nx">component</span><span class="o">=</span><span class="dl">'</span><span class="s1">p</span><span class="dl">'</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">TableHead</span> <span class="nx">component</span><span class="o">=</span><span class="dl">'</span><span class="s1">p</span><span class="dl">'</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">TableRow</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span><span class="o">&gt;</span><span class="nc">Dessert </span><span class="p">(</span><span class="mi">100</span><span class="nx">g</span> <span class="nx">serving</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Calories</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Fat</span><span class="o">&amp;</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Carbs</span><span class="o">&amp;</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="nx">Protein</span><span class="o">&amp;</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/TableRow</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/TableHead</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">TableBody</span><span class="o">&gt;</span> <span class="p">{</span><span class="nx">rows</span><span class="p">.</span><span class="nf">map</span><span class="p">((</span><span class="nx">row</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">(</span> <span class="o">&lt;</span><span class="nx">StyledTableRow</span> <span class="nx">key</span><span class="o">=</span><span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span><span class="o">&gt;</span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">component</span><span class="o">=</span><span class="dl">"</span><span class="s2">th</span><span class="dl">"</span> <span class="nx">scope</span><span class="o">=</span><span class="dl">"</span><span class="s2">row</span><span class="dl">"</span><span class="o">&gt;</span> <span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">name</span><span class="p">}</span> <span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">calories</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">fat</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">carbs</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="nx">StyledTableCell</span> <span class="nx">align</span><span class="o">=</span><span class="dl">"</span><span class="s2">right</span><span class="dl">"</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">row</span><span class="p">.</span><span class="nx">protein</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/StyledTableCell</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/StyledTableRow</span><span class="err">&gt; </span> <span class="p">))}</span> <span class="o">&lt;</span><span class="sr">/TableBody</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/Table</span><span class="err">&gt; </span> <span class="o">&lt;</span><span class="sr">/TableContainer</span><span class="err">&gt; </span> <span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p>Inspect the HTML elements:</p> <p><a href="proxy.php?url=https://i.sstatic.net/iHe2Tnj8.png" rel="noopener noreferrer"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.sstatic.net%2FiHe2Tnj8.png" alt="enter image description here"></a></p> <p><a href="proxy.php?url=https://stackblitz.com/edit/react-yw1rzp?file=Demo.tsx" rel="noopener noreferrer">stackblitz demo</a></p> javascript help react html Is there any way to monitor micro task and macro task queue? official_dulin Wed, 16 Aug 2023 04:35:26 +0000 https://dev.to/mrdulin/is-there-any-way-to-monitor-micro-task-and-macro-task-queue-fl9 https://dev.to/mrdulin/is-there-any-way-to-monitor-micro-task-and-macro-task-queue-fl9 <p>I am looking for a way to monitor/visualize the JavaScript micro task and macro task queue like this view in the <a href="proxy.php?url=https://stackoverflow.com/a/43897778/6463558">answer</a></p> <p>The microtask created by <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask"><code>queueMicrotask</code></a> API or Promise.</p> <p>The macro task created by <code>setTimeout()</code>.</p> <p>Is there any way?</p> javascript help node Pick and large interface V.S. small interface. Which one is better? official_dulin Tue, 29 Nov 2022 03:08:35 +0000 https://dev.to/mrdulin/pick-and-large-interface-vs-small-interface-which-one-is-better-2234 https://dev.to/mrdulin/pick-and-large-interface-vs-small-interface-which-one-is-better-2234 <p>When I use the option 1 to make my business types(<code>T0</code>, <code>T1</code>, etc...), I found I repeated <code>Pick&lt;ChannelBaseInfo1, 'channelCode'&gt;</code> too many. So maybe option 2 is better? Please give me some suggestion. Thanks.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="c1">// Option 1. </span> <span class="kd">type</span> <span class="nx">ChannelBaseInfo1</span> <span class="o">=</span> <span class="p">{</span> <span class="na">channelCode</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">channelName</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// Use Pick utility type</span> <span class="kd">type</span> <span class="nx">T0</span> <span class="o">=</span> <span class="nb">Pick</span><span class="o">&lt;</span><span class="nx">ChannelBaseInfo1</span><span class="p">,</span> <span class="dl">'</span><span class="s1">channelCode</span><span class="dl">'</span><span class="o">&gt;</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t0</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">T1</span> <span class="o">=</span> <span class="nx">ChannelBaseInfo1</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t50</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">T2</span> <span class="o">=</span> <span class="nb">Partial</span><span class="o">&lt;</span><span class="nb">Pick</span><span class="o">&lt;</span><span class="nx">ChannelBaseInfo1</span><span class="p">,</span> <span class="dl">'</span><span class="s1">channelCode</span><span class="dl">'</span><span class="o">&gt;&gt;</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t2</span><span class="p">:</span> <span class="kr">any</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// a lots of types using ChannelBaseInfo1 or Pick&lt;ChannelBaseInfo1, 'channelCode'&gt;</span> <span class="c1">// ...</span> <span class="kd">type</span> <span class="nx">T100</span> <span class="o">=</span> <span class="nb">Pick</span><span class="o">&lt;</span><span class="nx">ChannelBaseInfo1</span><span class="p">,</span> <span class="dl">'</span><span class="s1">channelName</span><span class="dl">'</span><span class="o">&gt;</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t100</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// Option 2.</span> <span class="kd">type</span> <span class="nx">ChannelCode</span> <span class="o">=</span> <span class="p">{</span> <span class="na">channelCode</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">ChannelName</span> <span class="o">=</span> <span class="p">{</span> <span class="na">channelName</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">ChannelBaseInfo2</span> <span class="o">=</span> <span class="nx">ChannelCode</span> <span class="o">&amp;</span> <span class="nx">ChannelName</span><span class="p">;</span> <span class="kd">type</span> <span class="nx">T101</span> <span class="o">=</span> <span class="nx">ChannelCode</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t101</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">T102</span> <span class="o">=</span> <span class="nx">ChannelBaseInfo2</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t102</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kd">type</span> <span class="nx">T103</span> <span class="o">=</span> <span class="nb">Partial</span><span class="o">&lt;</span><span class="nx">ChannelCode</span><span class="o">&gt;</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t103</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="c1">// a lots of types using ChannelBaseInfo2, ChannelCode and ChannelName</span> <span class="c1">// ...</span> <span class="kd">type</span> <span class="nx">T200</span> <span class="o">=</span> <span class="nx">ChannelName</span> <span class="o">&amp;</span> <span class="p">{</span> <span class="na">t200</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p><a href="proxy.php?url=https://www.typescriptlang.org/play?ssl=10&amp;ssc=2&amp;pln=11&amp;pc=1#code/PTAEHkAcBcEsHsB2oCMA6UAoaBPSBTUAYQAsBDRRfAGwCEyBnfASUQDN4VQBeUAb0yhQAY3KUaReABN8ALlANoAJ1iIA5gG5BIsVWoA5MgFs5C5as2YAvpkwhQAVSagACrGEBrUAFc41WLiguATYeIQAKgAMPK7uHgA8pBR69EysHCgANKAA5KLJEtL4OQB8oABk-NrQkfKKKupaNqEEoOFcvEnidIws7JwVVULQAKy1Zg2WzcERAEwxLmRKcGTU8W6eibo0qX0Z2Xnb1JIypWWVAsOz8hQ4Tbb2ZKDU8NAMoPBsQWHv3gwWxCOu3SA3gSlimy6KV6IKyuXy3ROxRKdjAaHRLQiKEi0V4GwSUJ2MP6cMOBQMxmRg0uQWx43qFnuqIgMAQyFmaExgPJSJiNIReiRdXMjWsXMJFJMfO0ApohhMwsm9xm3O6wP6806R15lQl8vwWi57UiHVVgqK1Oq2JQisZYqN2M1ZqJaQ1luGjttopsKuNAGYFksVmsJUjzkNaZE-V6psyni83h8vjNfv91M6eq6Mh9wfituT1ft4dqiqVmejOb7ZjiYnrKe6gtX6SLY5ggA" rel="noopener noreferrer">TypeScript Playground</a></p> emptystring Should the useState hook verify that the lazy initial state function must be synchronous official_dulin Thu, 13 Oct 2022 08:06:14 +0000 https://dev.to/mrdulin/should-usestate-validate-the-lazy-initial-state-function-must-be-a-synchronous-function-5bfh https://dev.to/mrdulin/should-usestate-validate-the-lazy-initial-state-function-must-be-a-synchronous-function-5bfh <p>By now, you can pass asynchronous function to the ReactJS <code>useState</code> hook to lazy initialize the state. But this seems not correct, the state will be a JavaScript promise.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">state</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">state</span><span class="p">);</span> <span class="c1">// "state" is a promise.</span> </code></pre> </div> <p>So I think ReactJS <code>useState</code> hook should validate this function to make sure it is synchronous. </p> <p>Something like this:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kd">const</span> <span class="nx">syncCallback</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">asyncCallback</span> <span class="o">=</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="dl">'</span><span class="s1">b</span><span class="dl">'</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">AsyncFunction</span> <span class="o">=</span> <span class="p">(</span><span class="k">async</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="p">}).</span><span class="kd">constructor</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">isAsync</span><span class="p">(</span><span class="nx">fn</span><span class="p">:</span> <span class="nb">Function</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">fn</span> <span class="k">instanceof</span> <span class="nx">AsyncFunction</span><span class="p">;</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">useState</span><span class="o">&lt;</span><span class="nx">S</span><span class="o">&gt;</span><span class="p">(</span><span class="nx">initialState</span><span class="p">:</span> <span class="nx">S</span> <span class="o">|</span> <span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">S</span><span class="p">))</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">initialState</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">function</span><span class="dl">'</span> <span class="o">&amp;&amp;</span> <span class="nx">isAsync</span><span class="p">(</span><span class="nx">initialState</span><span class="p">))</span> <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">'</span><span class="s1">initial state function must be synchronous.</span><span class="dl">'</span><span class="p">)</span> <span class="k">if</span> <span class="p">(</span><span class="nx">initialState</span> <span class="k">instanceof</span> <span class="nb">Function</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">initialState</span><span class="p">();</span> <span class="p">}</span> <span class="k">return</span> <span class="nx">initialState</span> <span class="p">}</span> <span class="kd">const</span> <span class="nx">state1</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="nx">syncCallback</span><span class="p">);</span> <span class="c1">// correct usage</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">state1: </span><span class="dl">'</span><span class="p">,</span> <span class="nx">state1</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">state2</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="nx">asyncCallback</span><span class="p">);</span> <span class="c1">// Only allow synchronous function, so it will throw error to developer.</span> </code></pre> </div> <p><a href="proxy.php?url=https://www.typescriptlang.org/play?#code/MYewdgzgLgBBCeZgGECGAbdAjVwDWMAvDABQCURAfDAOSo0DcAUKJLKgkmpjvkTB0TBSFQtRpZGTFuGgwAgp2AAxAK5IoAS3D8SgpCKowA3jAC+ZAHStoAJ1XAoIW8yYAzdY+1gYmiIqESNzAALhg1DW8KYyYYGFsAUyhVWx9g3zZUJASQNwUlCK9wZjNpD0idVQgEgGUoVCgEgB4ayhJNME0tDDqGhLCamAAfUnIjGrJo2N88kih4AAccvI6uzR76xqJCYhpyorAaGAAyY99-JXbO7vRexsmYKAALWxAAdxgwBI+AUVtX2wkGirG5wTYJGD7LQ6AC2VVgWAhShe4BAVUsNDI000sxB61u4Iy0CywGW4U80LAUzicUSyVSGTWGz65GYcVKtKSKR8eOZjSYpRkbDBfQAjPwqrVwSQlNxsLg8GRmDYQOgEpZ0CAAOZA4mNUVhGgAGhF+qVQrkeoSACYJdU7gk9LKMPL8OaVWqNdrdeDrYaTVbrUqYEA">TypeScript Playground</a></p> <p>What do you think? </p> react Is it a bad design to remove the setState callback? official_dulin Thu, 04 Aug 2022 07:54:00 +0000 https://dev.to/mrdulin/is-it-a-bad-design-to-remove-the-setstate-callback-dm1 https://dev.to/mrdulin/is-it-a-bad-design-to-remove-the-setstate-callback-dm1 <p>When we use React class component, there is a instance method <a href="proxy.php?url=https://reactjs.org/docs/react-component.html#setstate"><code>setState(updater[, callback])</code></a></p> <p><code>setState</code> callback which is guaranteed to fire after the update has been applied. </p> <p>However, when we use the <code>setState</code> method returned by <code>useState()</code> hook, it has no <code>callback</code> anymore.</p> <p>Let's compare <code>setState(updater[, callback])</code> and the <code>setState</code> returns by <code>useState()</code> hook.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="c1">// We can see all the logic at a glance</span> <span class="kd">const</span> <span class="nx">onIncreaseButtonClick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// step 1. Updating the state</span> <span class="kd">const</span> <span class="nx">nextState</span> <span class="o">=</span> <span class="p">{};</span> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">(</span><span class="nx">nextState</span><span class="p">,</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// step 2. call API</span> <span class="p">});</span> <span class="p">}</span> <span class="c1">//...</span> </code></pre> </div> <div class="highlight js-code-highlight"> <pre class="highlight tsx"><code><span class="kd">const</span> <span class="p">[</span><span class="nx">state</span><span class="p">,</span> <span class="nx">setState</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">();</span> <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// step 2. call API</span> <span class="p">},</span> <span class="p">[</span><span class="nx">state</span><span class="p">]);</span> <span class="c1">// We need to scroll the editor to the top to see the second part of the logic</span> <span class="c1">// 200 lines code</span> <span class="c1">// ...</span> <span class="c1">// ...</span> <span class="kd">const</span> <span class="nx">onIncreaseButtonClick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// step 1. Updating the state</span> <span class="kd">const</span> <span class="nx">nextState</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">setState</span><span class="p">(</span><span class="nx">nextState</span><span class="p">);</span> <span class="p">}</span> </code></pre> </div> <p>Do you see the difference? </p> <p>When the button click event occurs, we have two parts of logic: update state and call API.</p> <p>In the former case, the two parts of the logic code are organized together, and the execution order and code writing order are the same.</p> <p>In the latter case, the two parts of the logic are split in different places, the logic for updating the state is in the event handler and the logic for the call API is placed in <code>useEffect()</code>. And the order of execution and the order of reading (writing) is not the same.</p> <p>Some people have the same view as me, such as <a href="proxy.php?url=https://stackoverflow.com/questions/54954091/how-to-use-callback-with-usestate-hook-in-react">https://stackoverflow.com/questions/54954091/how-to-use-callback-with-usestate-hook-in-react</a></p> <p>That's why someone implements a <code>useStateCallback</code> hook.</p> <p>What do you think?</p> programming discuss react Use TypeScript interface extends and type composition semantically official_dulin Wed, 06 Jul 2022 06:55:49 +0000 https://dev.to/mrdulin/use-typescript-interface-extends-and-type-composition-semantically-24mf https://dev.to/mrdulin/use-typescript-interface-extends-and-type-composition-semantically-24mf <p>Consider below example, you have an API which accepts three parameters: <code>current</code>, <code>pageSize</code> and <code>channelCode</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kr">interface</span> <span class="nx">Pagination</span> <span class="p">{</span> <span class="nl">current</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">pageSize</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="p">}</span> <span class="kr">interface</span> <span class="nx">GetUsersByPageRequestDTO</span> <span class="p">{</span> <span class="nl">channelCode</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>Now you have two choice to pack these parameters.</p> <p><strong>Option 1:</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kr">interface</span> <span class="nx">GetUsersByPageRequestDTO</span> <span class="kd">extends</span> <span class="nx">Pagination</span> <span class="p">{</span> <span class="nl">channelCode</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p><strong>Option 2:</strong><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kd">type</span> <span class="nx">GetUsersByPageRequestDTO</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">channelCode</span><span class="p">?:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="o">&amp;</span> <span class="nx">Pagination</span><span class="p">;</span> </code></pre> </div> <p>Which one will you choose? Inheritance or composition?</p> <p>For me, I will choose option 2, use intersection type(type composition). Because using inheritance requires semantic correctness. Which means the two need to have <strong>is-a</strong> relationship. E.g. Bear is an animal. So bear interface can extend animal interface.</p> <p>Apparently, <code>GetUsersByPageRequestDTO</code> is <strong>NOT</strong> a <code>Pagination</code>. </p> <p>So we know the difference between <code>interface</code> and <code>type</code>, but we also have to pay attention to the semantic correctness, not only to get the final correct shape, right?</p> typescript discuss How to prevent other team member installing new npm package? official_dulin Fri, 24 Jun 2022 11:02:47 +0000 https://dev.to/mrdulin/how-to-prevent-other-team-member-installing-new-npm-package-5haa https://dev.to/mrdulin/how-to-prevent-other-team-member-installing-new-npm-package-5haa <p>Requirements:</p> <ul> <li>Only the project owner can install <strong>new</strong> npm package.</li> <li> <code>npm install</code> should work for local development and CI/CD pipeline.</li> <li>When other team member run <code>npm install &lt;PackageName&gt;</code> command, it should print an error message like: <code>"Installing new npm package is prevent, please contact the project owner."</code> </li> </ul> <p>Motivation: I don't want other team member install package at will. I want to limit this behavior through code or script.</p> <p>This is an optional-based question, Thanks for any advice.</p> npm programming discuss node Should event handlers be named using business actions or UI events? official_dulin Fri, 04 Mar 2022 06:55:31 +0000 https://dev.to/mrdulin/should-event-handlers-be-named-using-business-actions-or-ui-events-4mpi https://dev.to/mrdulin/should-event-handlers-be-named-using-business-actions-or-ui-events-4mpi <p>This problem is not specific to a framework, But I demonstrate questions in React.</p> <p>Example 1. Suppose the following is a user registration page component<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="kd">const</span> <span class="nx">Register</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// business actions</span> <span class="kd">const</span> <span class="nx">onUserRegistered</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">userService</span><span class="p">.</span><span class="nx">register</span><span class="p">();</span> <span class="p">};</span> <span class="c1">// Element event</span> <span class="kd">const</span> <span class="nx">onUserRegisteredFormSubmit</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">userService</span><span class="p">.</span><span class="nx">register</span><span class="p">();</span> <span class="p">};</span> <span class="k">return</span> <span class="p">(</span> <span class="p">&lt;</span><span class="nt">form</span> <span class="na">onSubmit</span><span class="p">=</span><span class="si">{</span><span class="nx">onUserRegistered</span><span class="si">}</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">type</span><span class="p">=</span><span class="s">"submit"</span><span class="p">&gt;</span>Register<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;/</span><span class="nt">form</span><span class="p">&gt;</span> <span class="p">);</span> <span class="p">};</span> </code></pre> </div> <p>Example 2. Suppose the following is a product create page component.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="kd">const</span> <span class="nx">NewProduct</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// business actions</span> <span class="kd">const</span> <span class="nx">onProductCreated</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">productService</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span> <span class="p">};</span> <span class="c1">// Element event</span> <span class="kd">const</span> <span class="nx">onProductCreateButtonClick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">productService</span><span class="p">.</span><span class="nx">create</span><span class="p">();</span> <span class="p">};</span> <span class="k">return</span> <span class="p">(</span> <span class="p">&lt;&gt;</span> <span class="p">&lt;</span><span class="nt">button</span> <span class="na">type</span><span class="p">=</span><span class="s">"button"</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="nx">onProductCreated</span><span class="si">}</span><span class="p">&gt;</span> Create <span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span> <span class="p">&lt;/&gt;</span> <span class="p">);</span> <span class="p">};</span> </code></pre> </div> <p>As you can see, I am hesitant to use business actions, or elements events to name event handlers. I prefer element events to name the event handler because this is the presentation layer, the presentation layer should not care about business actions. The service layer contains business actions. Please advice.</p> <p>Business action name composed by: <code>on</code> + <code>BusinessAction</code></p> <p>UI event name composed by: <code>on</code> + <code>WhichElement</code> + <code>Event</code> </p> react naming frontend discuss How would you name the TS interface like this(TS types naming convention)? official_dulin Wed, 23 Feb 2022 07:05:54 +0000 https://dev.to/mrdulin/how-would-you-name-the-ts-interface-like-this-3lpg https://dev.to/mrdulin/how-would-you-name-the-ts-interface-like-this-3lpg <p>For the API <code>/v1/users</code>, the data shape of the user is:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="err">name:</span><span class="w"> </span><span class="err">'teresa</span><span class="w"> </span><span class="err">teng'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p>For the API <code>/v1/user/:id</code>, the data shape of the user has some extra fields:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight json"><code><span class="p">{</span><span class="w"> </span><span class="err">id:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="err">name:</span><span class="w"> </span><span class="err">'teresa</span><span class="w"> </span><span class="err">teng'</span><span class="p">,</span><span class="w"> </span><span class="err">role:</span><span class="w"> </span><span class="err">'admin'</span><span class="p">,</span><span class="w"> </span><span class="err">email:</span><span class="w"> </span><span class="err">'[email protected]'</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre> </div> <p>I'm looking for good names for the interfaces. The names I've used are:</p> <p>Option 1:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kr">interface</span> <span class="nx">BaseUserEntity</span> <span class="p">{</span> <span class="nl">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kr">interface</span> <span class="nx">UserEntity</span> <span class="kd">extends</span> <span class="nx">BaseUserEntity</span> <span class="p">{</span> <span class="nl">role</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">email</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>I don't really like <code>Base</code> prefix for the interface name.</p> <p>Option 2:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code><span class="kr">interface</span> <span class="nx">UserEntity</span> <span class="p">{</span> <span class="nl">id</span><span class="p">:</span> <span class="kr">number</span><span class="p">;</span> <span class="nl">name</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> <span class="kr">interface</span> <span class="nx">UserDetailEntity</span> <span class="kd">extends</span> <span class="nx">UserEntity</span> <span class="p">{</span> <span class="nl">role</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="nl">email</span><span class="p">:</span> <span class="kr">string</span><span class="p">;</span> <span class="p">}</span> </code></pre> </div> <p>I think <code>User</code> and <code>UserDetail</code> are the same thing, introducing excess words - <code>Detail</code>. Do you have any suggestions for the naming convension? Thanks.</p> typescript help When I create a post, how to automatically complete the tag metadata? official_dulin Sat, 18 Sep 2021 06:15:07 +0000 https://dev.to/mrdulin/when-i-create-a-post-how-to-automatically-complete-the-tag-metadata-gn1 https://dev.to/mrdulin/when-i-create-a-post-how-to-automatically-complete-the-tag-metadata-gn1 <p>The current situation is that I don't know what tags are available at all unless I check from <a href="proxy.php?url=https://dev.to/tags">tags</a>. I don't know the exact name of the tag available either. For example, the tag about JavaScript is <code>js</code> or <code>javascript</code>? The tag about <code>Go</code> is <code>go</code> or <code>golang</code>?</p> <p>Or, the tag can be anything? Will <code>dev.to</code> create the new tag automatically? </p> <p>Is there any way to automatically complete the available tags?</p> help React & Redux Application Architecture official_dulin Thu, 05 Aug 2021 08:37:14 +0000 https://dev.to/mrdulin/react-redux-application-architecture-5f8f https://dev.to/mrdulin/react-redux-application-architecture-5f8f <p>Architecture based on React Hooks and React FC design:</p> <p><a href="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmrdulin%2Fpic-bucket-01%2F2114bc7c8166c9ba66734202a5d40f8a907d89ec%2FReact%2526Redux%25E5%25BA%2594%25E7%2594%25A8%25E6%259E%25B6%25E6%259E%2584%25E5%259B%25BE%2520.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fmrdulin%2Fpic-bucket-01%2F2114bc7c8166c9ba66734202a5d40f8a907d89ec%2FReact%2526Redux%25E5%25BA%2594%25E7%2594%25A8%25E6%259E%25B6%25E6%259E%2584%25E5%259B%25BE%2520.png" alt="React&amp;Redux application architecture"></a></p> <h2> View layer </h2> <p>React functional component to build the view, containing :</p> <ul> <li>ReactElement, the JSX view element</li> <li>Event handling functions for the view, such as onClick, etc.</li> <li>Use the hooks provided by the controller layer to get the View Model</li> </ul> <p>The view logic using the internal state of the component is encapsulated by a custom hook that exports the state and the functions to manipulate the state, and the event handling functions directly call the custom hook exported functions to change the state of the view.</p> <h2> Controller layer </h2> <p>Mainly using React hooks to implement, including</p> <ul> <li>Business custom hooks</li> <li>UI custom hooks</li> </ul> <p>UI custom hooks encapsulate the internal state of the component (defined by <code>useState</code>) and its change operations, the internal state of the component may depend on the component's props calculated by logic, all encapsulated in hooks, this piece of code logic should not be placed in the component.</p> <p>Business custom hooks encapsulate data and its operations related to business logic, data sources include backend service API call returns, web storage, cookies, constants, URL query parameter, etc. Need to persist data to the redux store data acquisition method using dispatch + redux-thunk created asynchronous action creator (redux-saga, etc.), considering that some views are very independent and do not need to persist API data to the redux store, you can omit dispatch + async action creator, directly call the front-end fetch wrapped API service directly to call the backend service API.<br> The data generated by user-view interaction may be persisted in the redux store, typically data such as filter conditions, obtained through <code>useSelector</code>+selector, with this redux state corresponding redux action operations are also encapsulated in the hook, through <code>useDispatch</code>+action creator for operations.</p> <h2> Data Access layer </h2> <p>Contains.</p> <ul> <li>Selector created by Reselect library, used to read data from redux store and calculate derived data</li> <li>Redux thunk (redux-saga) and other middleware created by thunk or saga, used for asynchronous process control, action meta data processing, call front-end API service, incoming verification and processing, to ensure that the parameters passed to the API service method is correct.</li> </ul> <p>Use the createSelector method provided by the reselect library to create a selector as a method to access the redux store. selector can be used either by <code>useSelector</code> or in redux-thunk by <code>xxxSelector(getState()</code>. which is used to get a certain state slice on the redux store.<br> Another purpose of selector is to provide optimization for computing derived data. selector can compute derived data based on the component's props and state, <a href="proxy.php?url=https://github.com/reduxjs/%20reselect#accessing-react-props-in-selectors" rel="noopener noreferrer">Accessing React Props in Selectors</a>, which can calculate derived data based on dynamic or non-dynamic arguments <a href="proxy.php?url=https://github.com/%20reduxjs/reselect#q-how-do-i-create-a-selector-that-takes-an-argument" rel="noopener noreferrer">How do I create a selector that takes an argument?</a>, the selector provides a memozie function that returns the result of the last calculation (equal references, equal values) with the same input, in conjunction with <code>React.memo</code>, <code>useEffect</code>'s dependency list skips the <code>effect</code> and uses <code>useMemo</code>. If the dependency list uses the derived data returned by the selector, the memorized result can be created while the reference and value of the returned result remain unchanged, avoiding the component to render re-executes expensive logic, completes the rendering optimization of the component, and reduces unnecessary re-render.</p> <p>Translated with <a href="proxy.php?url=http://www.DeepL.com/Translator" rel="noopener noreferrer">www.DeepL.com/Translator</a> (free version)</p> <h2> Service layer </h2> <p>A relatively broad category containing helper, utils, third-party libraries, generic custom hooks, third-party hooks, etc. dedicated to a specific task.<br> The main function is to interface to external data sources, backend API service, third-party APIs, websockets, etc. The communication protocol is mainly HTTP protocal, and the preprocessing of requests is done through interceptors. Pre-processing of requests, pre-processing of responses and error handling. No matter what external data source interface is called, the data structure of the output of the front-end API service should be fixed by a unified standard (pre-defined interface), for example, the output object contains three fields: <code>{error: null, result: null, message: null}</code>.<br> helper, utils store generic methods, do not care about and should not contain business logic, not to repeat.<br> The API service methods can be called in the controller layer hooks or in the async action creator created by redux thunk, not directly in the component view layer.</p> <h2> Data Persistence layer </h2> <p>The data stored in the Redux store is not considered persistent in the strict sense, as it is stored in the application memory and belongs to Memory DB, the life cycle is the life cycle of the application, the application is initialized (refresh the browser, start, restart the service), then the previously stored data is lost. Depending on the requirements decide whether to use libraries such as redux-presist to persist the data in Redux store to Web Storage.</p> <p>The main types of data stored are as follows.</p> <ul> <li>Business data from external data sources</li> <li>Data generated by user interaction with the View layer, such as forms, filter conditions, etc.</li> <li>Data from Web Storage and cookies to initialize the redux store, depending on the requirements</li> </ul> <p>Other data sources that the application depends on: Web Storage, cookies, URL query parameter, application-defined constants, etc. for the browser environment.</p> <p>The specific architecture is adjusted according to the requirements, and the separation of concerns is achieved through layering, partitioning, etc. Combined with componentization, modularization, high cohesion, low coupling, TDD to improve the quality of front-end code, improve readability, maintainability, scalability, reusability.</p> <p>Additional: components are divided into display components and container components, and container components can be subdivided into page level, component level, and according to the scope of the role can also be divided into page level, component level, it is customary to create hooks.ts in the directory where the component files are located to store the custom hooks needed for that level of components. The larger the scope, the more generic the hooks are, and the closer the files are to the root directory.</p> <p>Translated with <a href="proxy.php?url=http://www.DeepL.com/Translator" rel="noopener noreferrer">www.DeepL.com/Translator</a> (free version)</p> <p>Original article link: <a href="proxy.php?url=https://github.com/mrdulin/blog/issues/95" rel="noopener noreferrer">https://github.com/mrdulin/blog/issues/95</a></p> react redux architecture New Button for dev.to, mark article read. official_dulin Thu, 22 Apr 2021 10:44:45 +0000 https://dev.to/mrdulin/new-button-for-dev-to-mark-article-read-2ig0 https://dev.to/mrdulin/new-button-for-dev-to-mark-article-read-2ig0 <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--agxg6qVb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evu7b2t3kmncba3sw2wr.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--agxg6qVb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evu7b2t3kmncba3sw2wr.png" alt="image"></a></p> <p>A tampermonkey script for marking article read on dev.to. Use <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> to store your read list in your browser. So make sure your browser supports the IndexedDB feature.</p> <p>Q:Why I want to write this script?</p> <p>A: I often read articles on dev.to site, dev.to provides three buttons, namely like, unicorn and readinglist. But I want a button to indicate whether the current article has been read, similar to the read function of mailing lists and RSS. </p> <p>There are some articles that I don't like, unicorn, and don't want to add them to my readinglist. These articles are not useful to me. I want to know if I had read them, That's why I made this button. Just mark this kind of article as read. </p> <p>Another reason is <a href="proxy.php?url=https://dev.to/mrdulin/why-are-there-duplicate-subscription-emails-sent-to-me-461n">https://dev.to/mrdulin/why-are-there-duplicate-subscription-emails-sent-to-me-461n</a></p> <p>My mail subscription list will be repeated. Even though the subject of some mails is different, the articles pushed are the same, which makes me very inefficient. With this button, I open the article link and see if it has been read. If it's read, directly command + w to close the browser tab</p> <p>The article you have not read.</p> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--8Z5TDt5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/930m1ct2mqr0nwfhuxfs.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--8Z5TDt5C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/930m1ct2mqr0nwfhuxfs.png" alt="image"></a></p> <p>The article you marked as read. </p> <p><a href="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--CX1v7ik2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf9q7afpz07ssimq92ud.png" class="article-body-image-wrapper"><img src="proxy.php?url=https://res.cloudinary.com/practicaldev/image/fetch/s--CX1v7ik2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kf9q7afpz07ssimq92ud.png" alt="image"></a></p> <p>You can export and save the read list to your local file, GitHub Gist, etc... </p> <p>Install this script from here: <a href="proxy.php?url=https://greasyfork.org/en/scripts/425384-mark-post-read-on-dev-to">https://greasyfork.org/en/scripts/425384-mark-post-read-on-dev-to</a></p> <p>Source code: <a href="proxy.php?url=https://github.com/mrdulin/dl-toolkits/blob/master/scripts/mark-post-read-on-devto.js">https://github.com/mrdulin/dl-toolkits/blob/master/scripts/mark-post-read-on-devto.js</a></p> javascript technology productivity discuss