DEV Community: official_dulinThe 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.jpegDEV Community: official_dulin
https://dev.to/mrdulin
enWhy Material UI table component prop can violates w3c html element nesting rules?official_dulinTue, 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><Table/></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><Table/></code> and <code><TableHead /></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"><</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">></span>
<span class="o"><</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">></span>
<span class="o"><</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">></span>
<span class="o"><</span><span class="nx">TableRow</span><span class="o">></span>
<span class="o"><</span><span class="nx">StyledTableCell</span><span class="o">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></span><span class="nx">Calories</span><span class="o"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></span><span class="nx">Fat</span><span class="o">&</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></span><span class="nx">Carbs</span><span class="o">&</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></span><span class="nx">Protein</span><span class="o">&</span><span class="nx">nbsp</span><span class="p">;(</span><span class="nx">g</span><span class="p">)</span><span class="o"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/TableRow</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/TableHead</span><span class="err">>
</span> <span class="o"><</span><span class="nx">TableBody</span><span class="o">></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">=></span> <span class="p">(</span>
<span class="o"><</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">></span>
<span class="o"><</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">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</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">></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"><</span><span class="sr">/StyledTableCell</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/StyledTableRow</span><span class="err">>
</span> <span class="p">))}</span>
<span class="o"><</span><span class="sr">/TableBody</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/Table</span><span class="err">>
</span> <span class="o"><</span><span class="sr">/TableContainer</span><span class="err">>
</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>
javascripthelpreacthtmlIs there any way to monitor micro task and macro task queue?official_dulinWed, 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>
javascripthelpnodePick and large interface V.S. small interface. Which one is better?official_dulinTue, 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<ChannelBaseInfo1, 'channelCode'></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"><</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">></span> <span class="o">&</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">&</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"><</span><span class="nb">Pick</span><span class="o"><</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">>></span> <span class="o">&</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<ChannelBaseInfo1, 'channelCode'></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"><</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">></span> <span class="o">&</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">&</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">&</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">&</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"><</span><span class="nx">ChannelCode</span><span class="o">></span> <span class="o">&</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">&</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&ssc=2&pln=11&pc=1#code/PTAEHkAcBcEsHsB2oCMA6UAoaBPSBTUAYQAsBDRRfAGwCEyBnfASUQDN4VQBeUAb0yhQAY3KUaReABN8ALlANoAJ1iIA5gG5BIsVWoA5MgFs5C5as2YAvpkwhQAVSagACrGEBrUAFc41WLiguATYeIQAKgAMPK7uHgA8pBR69EysHCgANKAA5KLJEtL4OQB8oABk-NrQkfKKKupaNqEEoOFcvEnidIws7JwVVULQAKy1Zg2WzcERAEwxLmRKcGTU8W6eibo0qX0Z2Xnb1JIypWWVAsOz8hQ4Tbb2ZKDU8NAMoPBsQWHv3gwWxCOu3SA3gSlimy6KV6IKyuXy3ROxRKdjAaHRLQiKEi0V4GwSUJ2MP6cMOBQMxmRg0uQWx43qFnuqIgMAQyFmaExgPJSJiNIReiRdXMjWsXMJFJMfO0ApohhMwsm9xm3O6wP6806R15lQl8vwWi57UiHVVgqK1Oq2JQisZYqN2M1ZqJaQ1luGjttopsKuNAGYFksVmsJUjzkNaZE-V6psyni83h8vjNfv91M6eq6Mh9wfituT1ft4dqiqVmejOb7ZjiYnrKe6gtX6SLY5ggA" rel="noopener noreferrer">TypeScript Playground</a></p>
emptystringShould the useState hook verify that the lazy initial state function must be synchronousofficial_dulinThu, 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">=></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">=></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">=></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">=></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"><</span><span class="nx">S</span><span class="o">></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">=></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">&&</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>
reactIs it a bad design to remove the setState callback?official_dulinThu, 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">=></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">=></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">=></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">=></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>
programmingdiscussreactUse TypeScript interface extends and type composition semanticallyofficial_dulinWed, 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">&</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>
typescriptdiscussHow to prevent other team member installing new npm package?official_dulinFri, 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 <PackageName></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>
npmprogrammingdiscussnodeShould event handlers be named using business actions or UI events?official_dulinFri, 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">=></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">=></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">=></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"><</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">></span>
<span class="p"><</span><span class="nt">button</span> <span class="na">type</span><span class="p">=</span><span class="s">"submit"</span><span class="p">></span>Register<span class="p"></</span><span class="nt">button</span><span class="p">></span>
<span class="p"></</span><span class="nt">form</span><span class="p">></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">=></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">=></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">=></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"><></span>
<span class="p"><</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">></span>
Create
<span class="p"></</span><span class="nt">button</span><span class="p">></span>
<span class="p"></></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>
reactnamingfrontenddiscussHow would you name the TS interface like this(TS types naming convention)?official_dulinWed, 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>
typescripthelpWhen I create a post, how to automatically complete the tag metadata?official_dulinSat, 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>
helpReact & Redux Application Architectureofficial_dulinThu, 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&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>
reactreduxarchitectureNew Button for dev.to, mark article read.official_dulinThu, 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>
javascripttechnologyproductivitydiscuss