<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by 南小北 on Medium]]></title>
        <description><![CDATA[Stories by 南小北 on Medium]]></description>
        <link>https://medium.com/@nanxiaobei?source=rss-af9ba0e1b3fe------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*HJDDEnSg5q563jRn_RJCVw.jpeg</url>
            <title>Stories by 南小北 on Medium</title>
            <link>https://medium.com/@nanxiaobei?source=rss-af9ba0e1b3fe------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 22 Jun 2026 03:50:55 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@nanxiaobei/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Kuji — An Omikuji & Mindful Chat App]]></title>
            <link>https://nanxiaobei.medium.com/kuji-an-omikuji-mindful-chat-app-a813252efe57?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/a813252efe57</guid>
            <category><![CDATA[japan]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[japanese]]></category>
            <category><![CDATA[mindfulness]]></category>
            <category><![CDATA[zen]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Wed, 10 Jun 2026 15:58:17 GMT</pubDate>
            <atom:updated>2026-06-10T16:05:54.695Z</atom:updated>
            <content:encoded><![CDATA[<p>Draw a fortune, find peace within.</p><p>Close your eyes. Quiet your mind. Listen to the sound of a single leaf falling.</p><p>Kuji is a mindfulness app that weaves the ancient tradition of Japanese shrine fortune-drawing with modern AI-powered emotional wellness.</p><p>Amid the noise of everyday life, we’ve built you a quiet tearoom — warm, hand-drawn illustrations and a timeless Zen aesthetic to accompany every morning and evening of your year.</p><p>Link: <a href="https://apps.apple.com/app/id6758899526">https://apps.apple.com/app/id6758899526</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_Q_2eZhKF2OMNo1LBkYcyw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*i9drrJ14Q8ie-9COVton7g.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Kkki2jUY3uEPKe25tbIWIA.png" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a813252efe57" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Meet Arc Stage: Bring the Arc experience to Chrome]]></title>
            <link>https://nanxiaobei.medium.com/meet-arc-stage-bring-the-arc-experience-to-chrome-7e895004ae1f?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/7e895004ae1f</guid>
            <category><![CDATA[browsers]]></category>
            <category><![CDATA[extension]]></category>
            <category><![CDATA[cars]]></category>
            <category><![CDATA[chrome]]></category>
            <category><![CDATA[ux]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Tue, 24 Jun 2025 10:35:49 GMT</pubDate>
            <atom:updated>2025-06-25T12:55:15.869Z</atom:updated>
            <content:encoded><![CDATA[<p>If you’ve ever looked at the <a href="https://arc.net">Arc Browser</a> and thought, “Wow, I wish Chrome felt more like <em>that</em>,” you’re not alone — and now, you don’t have to switch browsers to get a taste.</p><p>Introducing <strong>Arc Stage</strong> — a Chrome extension inspired by Arc, designed to bring the sleek, focused, and beautiful experience of Arc to your good old Chrome browser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wF-qbtEmCcDcjJDRvl4N9A.png" /></figure><h3>🌈 What Is Arc Stage?</h3><p>Arc Stage gives you a Arc experience without leaving Chrome, just install a extension, that’s all.</p><p>→ <a href="https://chromewebstore.google.com/detail/angnbhooekkhlmohcmnjmlhfogjlfgee">Get Arc Stage on the Chrome Web Store</a></p><p>Here’s what it brings:</p><h3>✅ A Customizable Arc-like Background</h3><p>Set the vibe with a global background that makes every new tab feel like <em>your</em> space.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4TNTV0M20bfRHP8iAiVh2Q.png" /></figure><h3>✅ A Smart Dock for Quick Access</h3><p>Open tabs, bookmarks, and quick links — all from a minimal dock that’s just one click (or shortcut) away.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fu1qvHAgGmFiDOq0trJsFw.png" /></figure><h3>✅ Beautifully Redesigned Tabs, Bookmarks, etc.</h3><p>Arc Stage simplifies tab and bookmark access in a way that feels modern, fluid, and… just better.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pFkzcriCNw7izswwisddfg.png" /></figure><h3>🧠 Why Use Arc Stage?</h3><p>Let’s be honest: most of us use Chrome because it <em>just works</em>. But it can also feel, well, boring. Arc Stage adds just the right amount of flair and focus without asking you to change your habits or re-learn a new browser.</p><p>It’s Chrome, but elevated.</p><p>It’s your workspace, but more <em>you</em>.</p><p>It’s functional, aesthetic, and refreshingly fun.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zPqKbBt3ZTpuG8B3OZ84Cg.png" /></figure><h3>✨ Try It Today</h3><p>If you’ve ever been Arc-curious, or just want your Chrome to feel a little less… Chrome-y — give <strong>Arc Stage</strong> a spin. It takes 10 seconds to install, and your browsing experience might just never be the same again.</p><p>→ <a href="https://chromewebstore.google.com/detail/angnbhooekkhlmohcmnjmlhfogjlfgee">Get Arc Stage on the Chrome Web Store</a></p><p><strong>Built for people who love focus, style, and simplicity.</strong></p><p><strong>Welcome to a better-looking Chrome.</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7e895004ae1f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Easiest Way to Create Your Personal Bento Page]]></title>
            <link>https://nanxiaobei.medium.com/the-easiest-way-to-create-your-personal-bento-page-69650ce9bcf8?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/69650ce9bcf8</guid>
            <category><![CDATA[social-media]]></category>
            <category><![CDATA[bento]]></category>
            <category><![CDATA[personal]]></category>
            <category><![CDATA[link-in-bio]]></category>
            <category><![CDATA[webpage]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Tue, 19 Sep 2023 11:11:03 GMT</pubDate>
            <atom:updated>2023-09-19T11:11:03.012Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UUYB9srG2WbEa8oeT5lgcA.png" /></figure><p>In recent times, the trend of personalizing your online presence has taken the internet by storm.</p><p>Whether you’re a young student or a seasoned professional, having a unique online space to share your interests, hobbies, and achievements is becoming increasingly popular.</p><p>One of the easiest and most fun ways to create your own personal webpage is by using a service called “kee.so”.</p><p>In this article, we’ll guide you through simple steps on how to set up your very own bento page using <a href="https://kee.so/">kee.so</a>. This guide is specifically designed for young readers, so it’s super easy to understand!</p><h3>Step 1: Sign Up for kee.so</h3><p>The first step in creating your bento page is to sign up for an account on kee.so. It’s free and straightforward.</p><p>Go to <a href="https://kee.so/">kee.so</a> website, you’ll see a input box after “kee.so/”, to make it easy for others to find your bento page, you can choose a address that includes your name or a word that represents you, fill it in “yourname”.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LmpajX6SPaok7UNcjXmGcg.png" /></figure><p>Then click the “Launch →” button, and you’ll be guided through the registration process.</p><p>Make sure to choose a password you can remember easily. Enter you Email and receive the verification code to finish sign up successfully.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ce3OfsDiPCjO8kERAtOzsg.png" /></figure><h3>Step 2: Add Your Content</h3><p>Once you’re logged in, you’ll see a pop-up to fill in the social accounts you have. You can quickly initialize your page.</p><p>Now comes the fun part — adding your content!</p><p>Think of your bento page as a digital scrapbook. From the left panel, you can choose to add text, images, and links to your favorite websites.</p><p>Share your favorite hobbies, books, music, and anything else you love. Hove on the grid and click on the edit icon button, you can change it anytime.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vP7zeHyyyhW2OOhbpTrsqQ.png" /></figure><h3>Step 3: Customize Your Page</h3><p>kee.so offers various colorful and fun options for background and text on the right panel.</p><p>Find the one that best represents your personality and style. Don’t worry, you can always change it later if you change your mind!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h_f4FFglDfgTUYIph5BYiA.png" /></figure><h3>Step 4: Publish Your Page</h3><p>When you’re happy with how your bento page looks and what you’ve added, it’s time to publish it.</p><p>Click the “Publish” button on the top right corner(It’s only clickable when the page changes!), and voilà! Your personal bento page is now live on the internet for others to see.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6zPAN_fvPONRFqwnDjGRmQ.png" /></figure><h3><strong>Step 5: Share with Friends and Family</strong></h3><p>Click on the share bar at the bottom of the left panel, share the link of your bento page with your friends and family.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2BPStb8gsBe_Maj7TQPS7w.png" /></figure><p>They’ll love seeing what you’ve created. You can send it through email, share it on social media(Tweet it!), or even print it out and show it to them in person!</p><p>Creating your bento page with kee.so is a fantastic way to express yourself and share your interests with the world.</p><p>Remember, you can update and change your page whenever you want, so have fun and get creative.</p><p>Happy bento page building, young creators!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=69650ce9bcf8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[react-live-island, Dynamic Island for React]]></title>
            <link>https://nanxiaobei.medium.com/react-live-island-dynamic-island-for-react-84a71ec3c0a9?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/84a71ec3c0a9</guid>
            <category><![CDATA[nextjs]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[dynamic-island]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Mon, 18 Sep 2023 16:02:55 GMT</pubDate>
            <atom:updated>2023-09-18T16:02:55.663Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oHhJuYhpRQF3SGP9lrRysw.png" /></figure><p><strong>Product Hunt</strong>: <a href="https://www.producthunt.com/posts/react-live-island">https://www.producthunt.com/posts/react-live-island</a></p><p><strong>GitHub</strong>: <a href="https://github.com/nanxiaobei/react-live-island">https://github.com/nanxiaobei/react-live-island</a></p><pre>import LiveIsland from &#39;react-live-island&#39;;<br><br>const Demo = () =&gt; {<br>  return (<br>    &lt;LiveIsland&gt;<br>      {(isSmall) =&gt; (isSmall ? &#39;small&#39; : &#39;large&#39;)}<br>    &lt;/LiveIsland&gt;<br>  );<br>};</pre><p>I searched the entire internet but couldn’t find a suitable ⚛️ React Dynamic Island 🏝 component, so I decided to create one myself.</p><p>It is a very simple component that doesn’t introduce any external libraries. Simply importing it can add a dynamic island at the top of the page.</p><p>Considering that both the dynamic-island and react-dynamic-island NPM packages have been preempted, I named it react-live-island 🎉.</p><p>I hope you will like it, It is really very simple, directly importing and then you can see a dynamic island! ✌️ while also providing rich customization props.</p><p>Introducing react-live-island by 🐤 Kee (<a href="https://kee.so/">https://kee.so</a>)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=84a71ec3c0a9" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Introducing kee.so: Your home screen on internet]]></title>
            <link>https://nanxiaobei.medium.com/introducing-kee-so-your-home-screen-on-internet-dce26fa32e5f?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/dce26fa32e5f</guid>
            <category><![CDATA[resume]]></category>
            <category><![CDATA[linktree]]></category>
            <category><![CDATA[website-builders]]></category>
            <category><![CDATA[homescreen]]></category>
            <category><![CDATA[link-in-bio]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Mon, 31 Jul 2023 20:28:08 GMT</pubDate>
            <atom:updated>2023-08-04T19:06:59.375Z</atom:updated>
            <content:encoded><![CDATA[<h3>Introducing kee.so: Link in bio refresh, a home screen look</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qAK9e2y8cY54DdTYLFS-8Q.png" /></figure><blockquote><a href="https://kee.so/"><strong>https://kee.so/</strong></a></blockquote><blockquote><a href="https://www.producthunt.com/posts/kee-so"><strong>https://www.producthunt.com/posts/kee-so</strong></a></blockquote><h3>🐣 Initial Inspiration</h3><p>We originally wanted to develop kee.so, starting in August 2021.</p><p>At that time, we saw the release of iOS widgets, the videos released by the new UI of Windows 11 and Android 12, and we liked the different system styles very much.</p><p>So we decided to develop a service for creating a “personal page” based on these different UI tastes.</p><h3>🎙️ Original Idea of kee.so</h3><p>Linktree provides a display of “horizontal bars”, a one-dimensional space, from top to bottom, very simple.</p><p>kee.so wants to create a differentiated experience, so “grids” came to mind, changes from one dimension to two dimensions, can be arranged horizontally and vertically.</p><p>With the inspiration of apps and widgets, the personal page experience is enriched and more possibilities are created.</p><h3>📺 Launched Official Welcome Page</h3><p>In August 2021, we registered the kee.so domain and Twitter account <a href="https://twitter.com/_keeso">@_keeso</a>.</p><p>In September 2021, the welcome page was launched, and the slogan was “Your home screen on internet”. This is the <a href="https://twitter.com/_keeso/status/1433905254773317633">first tweet</a>.</p><p>A personal page that can freely combine apps and widgets, so we gave it the concept: “home screen”.</p><h3>🍱 Met Halfway Through … Bento</h3><p>But kee.so did not have the right time to start formal development. In December 2022, Bento went online.</p><p>Although the idea is earlier than Bento, but developed late, so it is hard to avoid being called a Bento-like service.</p><p>But still, kee.so is only similar in “grids”, it is completely different in other aspects.</p><h3>🪩 How kee.so Differs from Bento</h3><p>The core of kee.so is not built on the concept of “bento 🍱”, but on the concept of “home screen”.</p><p>So in essence, the underlying concepts of kee.so and Bento are different.</p><p>What kee.so wishes to provide is a “home screen”.</p><h3>📱 “Home Screen” x Web</h3><p>A personal page that is identical in concept and design like a home screen of a phone.</p><p>It’s one page, one link, one “home screen” for everyone.</p><p>With the form of app + widgets, everyone can easily create a home screen on internet, which can be freely customized just like the home screen of a phone.</p><h3>🍧 Unlimited Personal Page</h3><p>This page focuses on displaying your various social links, URLs, pictures, and text.</p><p>This page link, uniquely defined by you, can be placed on any social platform, Instagram, Twitter, TikTok…</p><p>The design of the page is up to you to create freely, emoji icon, widget background color, home screen “wallpaper”, blurring, adding noise, you will not encounter any problems.</p><h3>🐥 Your Home Screen on Internet</h3><p>The degree of freedom and personalization of kee.so is far beyond that of Bento.</p><p>On mobile, its interface is completely responsive and does not require additional design, which is exactly the meaning of the web.</p><p>Be consistent on any screen and insist on consistent performance.</p><p>Make the only home screen, Perfect in everywhere. ⚡️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dce26fa32e5f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Watch, a more elegant way to monitor antd Form field changes]]></title>
            <link>https://nanxiaobei.medium.com/watch-a-more-elegant-way-to-monitor-antd-form-field-changes-7c9b12457d67?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/7c9b12457d67</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[ant-design]]></category>
            <category><![CDATA[forms]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[hooks]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Wed, 07 Dec 2022 10:20:10 GMT</pubDate>
            <atom:updated>2022-12-07T17:34:37.536Z</atom:updated>
            <content:encoded><![CDATA[<p>When using antd Form, it is a very common requirement to monitor the change of a certain field and render different UI according to different field values.</p><p>So in antd Form, how to monitor the change of a certain field?</p><h3>1. form.getFieldValue</h3><p>Before antd@4.20.0, assuming that we want to monitor the changes of the song field, we can easily write code like this:</p><pre>const [form] = Form.useForm();<br>const songValue = form.getFieldValue(&#39;song&#39;);<br><br>&lt;Form form={form}&gt;<br>  &lt;Form.Item label=&quot;Song&quot; name=&quot;song&quot;&gt;<br>    &lt;Input /&gt;<br>  &lt;/Form.Item&gt;<br>  {songValue?.length &gt; 0 &amp;&amp; &lt;div&gt;Song: {songValue}&lt;/div&gt;}<br>&lt;/Form&gt;;</pre><p>What’s wrong with using form.getFieldValue?</p><p>The problem is that the value obtained by form.getFieldValue does not trigger UI updates. Simply put, it is not a state (the behavior of Form has changed with the upgrade of antd, and the behavior of old versions will not be discussed here).</p><p>But why sometimes, you can see the UI updated?</p><p>This is because in the actual business code, multiple interfaces may be requested (setState multiple times), and the Form may be updated by the parent to trigger re-render (in short, we all know that the number of re-renders of a React component is very high. Uncontrollable, especially when the code is poorly written 😜).</p><p>So it is not songValue that triggers the UI update, but in the new re-render, songValue is also updated.</p><p>This is a point that is very prone to bugs. Maybe the UI is normal during development, but as mentioned above, “the number of re-renders is very uncontrollable”, maybe a certain re-render is not triggered, and the UI related to songValue is also It will not be updated.</p><h3>2. useState</h3><p>We found a bug where the UI doesn’t update! So I naturally thought of: turn song into a state.</p><pre>const [form] = Form.useForm();<br>const [songValue, setSongValue] = useState(&#39;&#39;);<br><br>&lt;Form form={form}&gt;<br>  &lt;Form.Item label=&quot;Song&quot; name=&quot;song&quot;&gt;<br>    &lt;Input onChange={(event) =&gt; setSongValue(event.target.value)} /&gt;<br>  &lt;/Form.Item&gt;<br>  {songValue?.length &gt; 0 &amp;&amp; &lt;div&gt;Song: {songValue}&lt;/div&gt;}<br>&lt;/Form&gt;;</pre><p>What’s wrong with using useState?</p><p>Put it simply, songValue here is not responsive, when song is updated using the built-in method of Form, the UI related to songValue will not be updated.</p><p>For example, when song is related to props changes, or related to interface data changes, when using form.setFieldsValue, form.resetFields, etc. to update form data, songValue will not be updated.</p><p>At this time, it is necessary to trigger setSongValue at the place where form.setFieldsValue and so on are executed.</p><p>When form.setFieldsValue is executed in a very high parent component, it is necessary to lifting state songValue up. To avoid this trouble, we prefer to handle changes in the child component’s useEffect.</p><p>Using useEffect not only triggers an extra re-render, but what if there are many fields that need to be processed in multiple places? (common situation in actual development)</p><p>We need to add a lot of repetitive code, write it back and forth, and forget where it is not added, where it needs to be added, and where it does not need to be added. In the end, the update logic will become a mess.</p><h3>3. Form.useWatch</h3><p>At the beginning, it was emphasized before antd@4.20.0, because from antd@4.20.0, antd Form added a new API Form.useWatch to handle this situation.</p><p>At this point, songValue can respond to the updates of form.setFieldsValue, form.resetFields, etc.</p><pre>const [form] = Form.useForm();<br>const songValue = Form.useWatch(&#39;song&#39;, form);<br><br>&lt;Form form={form}&gt;<br>  &lt;Form.Item label=&quot;Song&quot; name=&quot;song&quot;&gt;<br>    &lt;Input /&gt;<br>  &lt;/Form.Item&gt;<br>  {songValue?.length &gt; 0 &amp;&amp; &lt;div&gt;Song: {songValue}&lt;/div&gt;}<br>&lt;/Form&gt;;</pre><p>What’s wrong with using Form.useWatch? (Why is there still a problem!)</p><p>In fact, it is not a problem, the main thing is that the performance is not good. Because Form.useWatch actually turns songValue into a state, and then handles the form linkage internally.</p><p>But the problem with state is that it will trigger the re-render of the entire component and perform unnecessary diffs. If the component is large and listens to Input for real-time input, this kind of performance consumption is terrible, and each key is one time. Full diff.</p><p>And this kind of re-render is actually meaningless, because we “precisely” know that we need to monitor the changes of the song field, and update the &quot;partial&quot; UI according to the value of song instead of updating the overall UI.</p><p>So is there a more elegant “partial update” solution?</p><h3>4. Watch, from Ant Plus 5</h3><p>Ant Plus 5 (antx) provides a Watch component, which is dedicated to listening to changes in form fields and updating local UI requirements.</p><p>Using the antx component can simplify the antd Form code, then the code to listen to song will be as follows:</p><pre>import { Form, Watch, Input } from &#39;antx&#39;;<br><br>const [form] = Form.useForm();<br>&lt;Form form={form}&gt;<br>  &lt;Input label=&quot;Song&quot; name=&quot;song&quot; /&gt;<br>  &lt;Watch name=&quot;song&quot;&gt;<br>    {(songValue) =&gt; {<br>      // Only update in this place,<br>      // not trigger the whole component re-render every time<br>      return songValue?.length &gt; 0 &amp;&amp; &lt;div&gt;Song: {songValue}&lt;/div&gt;;<br>    }}<br>  &lt;/Watch&gt;<br>&lt;/Form&gt;;</pre><p>By using Watch, you can avoid the performance problem of Form.useWatch non-stop full re-render, and at the same time, you don’t need to process the update logic in useEffect.</p><p>Using Watch, only the UI returned in the render props will be updated, and the entire component will not be linked to re-render continuously.</p><p>Online example → <a href="https://codesandbox.io/s/antx-v4hqw">https://codesandbox.io/s/antx-v4hqw</a></p><h3>5. Introducing Watch props</h3><p>Watch can also use list prop to watch multiple fields.</p><p>name and list are mutually exclusive, because name(NamePath) of antd also supports array form, so list is used to distinguish different meanings of arrays.</p><p>children &amp; onlyValid are mutually exclusive with onChange.</p><p>Use onlyValid to get only &quot;valid values&quot; that are not undefined in the children function. And setState can be executed in onChange.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/849/1*lq4pb8XV4QSscx9it_Hojg@2x.png" /></figure><p>Welcome to the Watch component of antx.</p><p>For more information about Ant Plus 5, please check → <a href="https://github.com/nanxiaobei/ant-plus">https://github.com/nanxiaobei/ant-plus</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7c9b12457d67" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ant Plus 5, Ant Design Form Simplified, make forms easier]]></title>
            <link>https://nanxiaobei.medium.com/ant-plus-5-ant-design-form-simplified-make-forms-easier-c04ce67cce6b?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/c04ce67cce6b</guid>
            <category><![CDATA[forms]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[ant-design]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Tue, 06 Dec 2022 10:14:39 GMT</pubDate>
            <atom:updated>2022-12-06T16:42:46.542Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6UNCpL2wyCLheRYVoOqZNg.png" /></figure><p>Ant Design Form Simplified, build Form in the simplest way.</p><p>Now Ant Plus 5 is released, redesigned and rewritten with TypeScript.</p><h3>GitHub</h3><p><a href="https://github.com/nanxiaobei/ant-plus">https://github.com/nanxiaobei/ant-plus</a></p><h3>Feature</h3><ul><li>Say goodbye to cumbersome &lt;Form.Item&gt; and rules</li><li>Full TypeScript hinting support</li><li>Easily extend existing form components</li></ul><h3>Installation</h3><pre>pnpm add antx<br># or<br>yarn add antx<br># or<br>npm i antx</pre><h3>Usage</h3><pre>import { Button, Form, Input, Select, WrapperCol } from &#39;antx&#39;;<br><br>const App = () =&gt; {<br>  return (<br>    &lt;Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }}&gt;<br>      &lt;Input label=&quot;Name&quot; name=&quot;name&quot; rules={[&#39;required&#39;, &#39;max=10&#39;]} /&gt;<br>      &lt;Select<br>        label=&quot;Gender&quot;<br>        name=&quot;gender&quot;<br>        rules={[&#39;required&#39;]}<br>        options={[<br>          { value: 1, label: &#39;Male&#39; },<br>          { value: 2, label: &#39;Female&#39; },<br>        ]}<br>      /&gt;<br>      &lt;InputNumber<br>        label=&quot;Age&quot;<br>        name=&quot;age&quot;<br>        rules={[&#39;required&#39;, &#39;number&#39;, &#39;min=0&#39;]}<br>      /&gt;<br>      &lt;WrapperCol&gt;<br>        &lt;Button type=&quot;primary&quot; htmlType=&quot;submit&quot;&gt;<br>          Submit<br>        &lt;/Button&gt;<br>      &lt;/WrapperCol&gt;<br>    &lt;/Form&gt;<br>  );<br>};<br>export default App;</pre><h3>Introduction</h3><p>antx provides a set of antd enhanced form field components, features of enhanced components:</p><p><strong>1. No need to write </strong><strong>&lt;Form.Item&gt;</strong><br>Directly mix Form.Item props with the original field component props (full TypeScript hints), which greatly simplifies the code.</p><p><strong>2. Simplified </strong><strong>rules (only enhanced, original </strong><strong>rules is also supported)</strong><br>Provide rules in string phrase, for example rules={[&#39;required&#39;, &#39;max=10&#39;]} represents for rules={[{ required: true }, { max: 10 }]}.</p><p><strong>3. Not add any other props</strong><br>All props are antd original props, without adding any other props and APIs, reducing mental burden.</p><p>In addition, antx also provides 3 original components (Form, Button, Item), 2 custom components (WrapperCol, Watch), and a tool function create.</p><h3>API</h3><h4>1. Enhanced field components</h4><blockquote><em>1st-level field components:</em></blockquote><ul><li><strong>AutoComplete</strong></li><li><strong>Cascader</strong></li><li><strong>Checkbox</strong></li><li><strong>DatePicker</strong></li><li><strong>Input</strong></li><li><strong>InputNumber</strong></li><li><strong>Mentions</strong></li><li><strong>Radio</strong></li><li><strong>Rate</strong></li><li><strong>Select</strong></li><li><strong>Slider</strong></li><li><strong>Switch</strong></li><li><strong>TimePicker</strong></li><li><strong>Transfer</strong></li><li><strong>TreeSelect</strong></li><li><strong>Upload</strong></li></ul><blockquote><em>2nd-level field components, in </em><em>antd is </em><em>AAA.BBB, and in </em><em>antx can directly import </em><em>BBB:</em></blockquote><ul><li><strong>CheckboxGroup</strong> Checkbox.Group</li><li><strong>DateRange</strong> DatePicker.RangePicker</li><li><strong>TextArea</strong> Input.TextArea</li><li><strong>Search</strong> Input.Search</li><li><strong>Password</strong> Input.Password</li><li><strong>RadioGroup</strong> Radio.Group</li><li><strong>TimeRange</strong> TimePicker.RangePicker</li><li><strong>Dragger</strong> Upload.Dragger</li></ul><h4>2. Base components</h4><blockquote><em>Form, </em><em>Button, and </em><em>Item are </em><em>antd original components, provided for convenience. </em><em>Watch and </em><em>WrapperCol are custom components.</em></blockquote><ul><li><strong>Form</strong></li><li><strong>Button</strong></li><li><strong>Item</strong> Form.Item</li><li><strong>Watch</strong> used to monitor the changes of form fields, which can be only partial re-render, more refined and better performance</li></ul><pre>// Watch usage example<br>import { Watch } from &#39;antx&#39;;<br><br>&lt;Form&gt;<br>  &lt;Input label=&quot;Song&quot; name=&quot;song&quot; /&gt;<br>  &lt;Watch name=&quot;song&quot;&gt;<br>    {(song) =&gt; {<br>      return &lt;div&gt;Song: {song}&lt;/div&gt;;<br>    }}<br>  &lt;/Watch&gt;<br>&lt;/Form&gt;;</pre><ul><li><strong>WrapperCol</strong> simplify the layout code, the same props as Form.Item, used when the UI needs to be aligned with the input box.</li></ul><pre>// WrapperCol usage example<br>import { WrapperCol } from &#39;antx&#39;;<br><br>&lt;Form&gt;<br>  &lt;Input label=&quot;Song&quot; name=&quot;song&quot; /&gt;<br>  &lt;WrapperCol&gt;This is a hint that aligns with the input box&lt;/WrapperCol&gt;<br>&lt;/Form&gt;;</pre><h4>3. create tool function</h4><ul><li>create convert existing form field components into components that support Form.Item props mix-in, easily extend existing components.</li></ul><pre>import { create } from &#39;antx&#39;;<br><br><br>// After expansion (TypeScript hints support)<br>const MyCustomInputPlus = create(MyCustomInput);<br><br>&lt;Form&gt;<br>  &lt;MyCustomInputPlus label=&quot;Song&quot; name=&quot;song&quot; rules={[&#39;required&#39;]} /&gt;<br>&lt;/Form&gt;;</pre><h4>4. Simplified rules</h4><pre>// Simplified rules usage example<br><br>&lt;Form&gt;<br>  &lt;Input label=&quot;Song&quot; name=&quot;song&quot; rules={[&#39;required&#39;, &#39;min=0&#39;, &#39;max=50&#39;]} /&gt;<br>&lt;/Form&gt;</pre><h3>Comparison</h3><p>Ant Plus and Ant Design form code comparison:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*guFoxC3ly7TzFB7yGpvGQQ.png" /></figure><h3>Try</h3><p>Welcome to try → <a href="https://github.com/nanxiaobei/ant-plus">https://github.com/nanxiaobei/ant-plus</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c04ce67cce6b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Introducing hugo-paper 6.0]]></title>
            <link>https://nanxiaobei.medium.com/introducing-hugo-paper-6-0-f35f0b5e0e4c?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/f35f0b5e0e4c</guid>
            <category><![CDATA[tailwind-css]]></category>
            <category><![CDATA[hugo]]></category>
            <category><![CDATA[themes]]></category>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[design]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Tue, 19 Jul 2022 17:40:44 GMT</pubDate>
            <atom:updated>2022-07-19T17:40:44.708Z</atom:updated>
            <content:encoded><![CDATA[<h3>Paper</h3><p>hugo-paper is a Hugo theme inspired by the paper reading experience to provide the best rendering of text.</p><p>There are currently 900+ GitHub Stars, and users from 13 countries around the world have submitted language packs.</p><p>Paper 6.0 is now officially released, refactored with Tailwind CSS, and redesigned.</p><h3>Demo</h3><p>Let’s take a look at the effect first: <a href="https://hugo-paper.vercel.app/">https://hugo-paper.vercel.app/</a></p><h3>GitHub</h3><p>View the source code of the theme: <a href="https://github.com/nanxiaobei/hugo-paper">https://github.com/nanxiaobei/hugo-paper</a></p><h3>6.0</h3><p>- Thoroughly refactored using Tailwind CSS to embrace the latest development experience</p><p>- Redesigned UI, single-column + rounded corners + shadow to enhance the page’s agility</p><p>- Support to display personal information (avatar, nickname and profile) on the home page, more customized</p><p>- Optional monochrome Dark Mode button</p><p>- Added support for RSS link icon</p><p>In short, while retaining the existing design elements, it provides a new UI experience and more customization.</p><h3><strong>Screenshots</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V83VGQvBC74es4hpmrVqRw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3B0n5-LNg8ztvA-aMj5axg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7e0Mbn1LyP9WjMF4Z1OVYg.png" /></figure><h3>The End</h3><p>hugo-paper 6.0 , welcome Star , welcome to try.</p><p>▷ 6.0 <a href="https://github.com/nanxiaobei/hugo-paper">https://github.com/nanxiaobei/hugo-paper</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f35f0b5e0e4c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[ Turn React into SolidJS, update on demand, no more re-render]]></title>
            <link>https://nanxiaobei.medium.com/turn-react-into-solidjs-update-on-demand-no-more-re-render-3230fe2f878c?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/3230fe2f878c</guid>
            <category><![CDATA[hooks]]></category>
            <category><![CDATA[solid]]></category>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Wed, 06 Apr 2022 19:38:11 GMT</pubDate>
            <atom:updated>2022-04-06T19:49:24.329Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*13q-duVlpfXxGzPUthTPdw.png" /></figure><h3>1. What is SolidJS?</h3><p>We all know SolidJS, if not, why are you reading this article!</p><p>We all know SolidJS, here is its doc: <a href="https://www.solidjs.com/">https://www.solidjs.com</a></p><p>Put it simply, SolidJS is the real “react” version of React, which is completely updated on demand, and where data changes are updated.</p><p>For example, a component:</p><pre>function App() {<br>  const [value, setValue] = useState(0);<br>  return &lt;div&gt;{value}&lt;/div&gt;;<br>}</pre><p>React calls the entire function of App to death (i.e. re-render), while SolidJS only updates the small piece of value.</p><p>Of course, SolidJS is like this:</p><pre>function App() {<br>  const [value, setValue] = createSignal(0);<br>  return &lt;div&gt;{value()}&lt;/div&gt;;<br>}</pre><p>In SolidJS, App is called only once during initialization, and is not executed after that.</p><p>So JSX in SolidJS is equivalent to “static template”, which is only used to describe the UI and will not be called again, and there is no diff.</p><p>That is to say, any function executed in App or any function executed in JSX will only be triggered once.</p><h3>2. First declare</h3><p>How to turn React into SolidJS?</p><p>Certainly not rename solid-js to react, nor manually use DOM API to update it without React logic.</p><p>It must be stated here:</p><p>The following implementation is completely based on the React API, rather than hacking with the DOM API or jQuery, which would be meaningless.</p><h3>3. How to implement it?</h3><h4>1. How to update only the small piece of value()?</h4><p>This is the core of the implementation idea, let’s just say it — that is to turn value() into a component.</p><p>Yes, it displays data, but it’s actually a component. It’s a component that just returns data.</p><h4>2. Why value() instead of value?</h4><p>Because we need to know that there is a data here, and it has to be updated later, how do we know?</p><p>According to JS syntax, there is no other way than state.value (use getter) or value() (call function).</p><p>This is why SolidJS must be written as value(). If it is written as value, God don&#39;t know how to update it, because in the implementation of &quot;static template&quot;, the function will not run again.</p><h4>3. Implement a useSignal similar to createSignal</h4><p>We want to implement a useSignal, similar to SolidJS&#39; createSignal, which returns two functions, a getter and a setter.</p><p>At the same time, the return of the getter is a component.</p><pre>function useSignal(val) {<br>  const valRef = useRef(val);<br>  const update = useRef();</pre><pre>  const Render = () =&gt; {<br>    const [value, setValue] = useState(valRef.current);<br>    update.current = setValue;<br>    return value;<br>  };</pre><pre>  const getter = () =&gt; {<br>    try {<br>      useState(); // Use this hack to know whether the data is in JSX or read normally elsewhere<br>      return &lt;Render /&gt;;<br>    } catch (e) {<br>      return valRef.current;<br>    }<br>  };</pre><pre>  const setter = (newVal) =&gt; {<br>    valRef.current = newVal;<br>    update.current(newVal);<br>  };</pre><pre>  return [getter, setter];<br>}</pre><p>The above is a minimal implementation, but it is problematic because the data may be used in multiple places, and the above can only update the data in the last place.</p><h4>4. Data synchronization update version useSignal</h4><p>Collect the update functions with an array of listeners and that&#39;s it. In fact, this is also the implementation idea of React state managers.</p><pre>function useSignal(val) {<br>  const valRef = useRef(val);<br>  const listeners = useRef([]);</pre><pre>  const Render = () =&gt; {<br>    const [value, setValue] = useState(valRef.current);</pre><pre>    useEffect(() =&gt; {<br>      listeners.current.push(setValue);<br>      return() =&gt; {<br>        listeners.current.splice(listeners.current.indexOf(setValue), 1);<br>      };<br>    }, []);</pre><pre>    return value;<br>  };</pre><pre>  return [<br>    () =&gt; {<br>      try {<br>        useState();<br>        return &lt;Render /&gt;;<br>      } catch (e) {<br>        return valRef.current;<br>      }<br>    },<br>    (payload) =&gt; {<br>      listeners.current.forEach((listener) =&gt; {<br>        listener((prev) =&gt; {<br>          valRef.current =<br>            typeof payload === &#39;function&#39; ? payload(prev) : payload;<br>          return valRef.current;<br>        });<br>      });<br>    },<br>  ];<br>}</pre><p>The above is already a working implementation.</p><p>At this point, the core of the story has actually been told.</p><p>But if it is to be really used for development needs, there is still a lot of unfinished business.</p><h3>4. What else is there to do?</h3><p>If it’s really “available”, it should at least implement:</p><ul><li>createEffect (for listening for data updates)</li><li>createMemo (for creating computed data)</li><li>onMount (for sending requests)</li><li>onCleanup (for unsubscribing)</li><li>What if the data is an object or an array? (This is the most complicated, the above actually only considers primitive data types)</li><li>How to implement conditional operator or function calls in JSX? (The conditional operator or function is only executed once during initialization and cannot respond to change)</li><li>How to respond to HMR? What if the data doesn’t appear in JSX for the first time? How to unsubscribe after component unmount…</li></ul><h3>5. Introducing solid-react</h3><p>There are a bunch of questions written on it, and naturally the answer is ready… This answer is called solid-react.</p><p>All the problems mentioned above have been solved. If you have a deeper understanding, you can look at the source code.</p><p>☞ GitHub: <a href="https://github.com/nanxiaobei/solid-react">https://github.com/nanxiaobei/solid-react</a></p><p>Here is the API for solid-react:</p><ul><li>useSignal (corresponding to createSignal, used to create data)</li><li>useUpdate (corresponding to createEffect, used to monitor data updates)</li><li>useAuto (corresponding to createMemo, used to create computed data)</li><li>useMount (corresponding to onMount, used to send requests)</li><li>useCleanup (corresponding to onCleanup, used to unsubscribe)</li><li>the data is an object or an array (use a proxy to handle this tricky case)</li><li>Run (for conditional operator or functions in JSX, Run(() =&gt; fn(value()))</li></ul><p>Please pay attention to the naming of the API, which is also said: try not to conflict with existing APIs (such as not directly naming useState useMemo, which will confuse the code), while keeping it concise enough (easy to write) and intuitive (easy to understand).</p><p>For specific API introduction, please check README: <a href="https://github.com/nanxiaobei/solid-react">https://github.com/nanxiaobei/solid-react</a></p><p>In this way, most common development scenarios can already be covered, that is, it can be used for “production”.</p><h3>6. Try solid-react</h3><p>Demo: <a href="https://codesandbox.io/s/solid-react-rymhr6?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;file=/src/App.js">https://codesandbox.io/s/solid-react-rymhr6?fontsize=14&amp;hidenavigation=1&amp;theme=dark&amp;file=/src/App.js</a></p><p>Here is a demo, you can open the console, click the button to try, and you will find:</p><p>Components don’t re-render anymore, React is completely SolidJS-style on-demand updates!</p><p>useUpdate useAuto does not need anything like deps, its dependencies are automatically learned. And only when dependencies change, they execute again.</p><p>Yes, that is to say, you can get rid of Hooks, useCallback useMemo deps memo, will it trigger re-render, it&#39;s all unnecessary.</p><p>A function is a function, an object is an object, and it will not be created again if it is written there.</p><h3>7. What else?</h3><p>solid-react is an experimental project, just to implement an idea, and in fact it is not bad.</p><p>solid-react tries its best to make it &quot;completely capable&quot;, whether it is sending requests or monitoring data, sparrows are small (but delicious) and have all the internal organs.</p><p>solid-react is a small thing, it may have flaws, of course it can&#39;t compare with the maturity of developing directly with React, and it is impossible to compare.</p><p>solid-react is definitely fine for small demo projects, but I haven&#39;t practiced it in big projects, it&#39;s good to play with it first, if you&#39;re interested.</p><p>solid-react is more like a concept. It is impossible for React officials to take this road, but thanks to the open source, you can experiment on this road yourself.</p><p>solid-react works hard to &quot;suffer from Hooks&quot;, a general confusion in the industry that has not disappeared for several years (although I feel Hooks are fine)</p><p>solid-react welcomes those who are interested to try it together and create more possibilities.</p><p>Turn React into SolidJS, say goodbye to Hooks, say goodbye to re-render ↓↓↓</p><p><a href="https://github.com/nanxiaobei/solid-react">https://github.com/nanxiaobei/solid-react</a></p><blockquote><em>Some people may want to say, isn’t the API of solid-react are Hooks? How to say goodbye to Hooks! In fact, the above is to be compatible with the mixed use of React and solid-react … Yes, I even considered this situation 🙈</em></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3230fe2f878c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[resso, world’s simplest React state manager]]></title>
            <link>https://nanxiaobei.medium.com/resso-worlds-simplest-react-state-manager-a3b1b0ccaa99?source=rss-af9ba0e1b3fe------2</link>
            <guid isPermaLink="false">https://medium.com/p/a3b1b0ccaa99</guid>
            <category><![CDATA[state-management]]></category>
            <category><![CDATA[hooks]]></category>
            <category><![CDATA[store]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux]]></category>
            <dc:creator><![CDATA[南小北]]></dc:creator>
            <pubDate>Thu, 17 Feb 2022 16:04:25 GMT</pubDate>
            <atom:updated>2024-01-17T19:52:23.771Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rsT4dzWTaQP70-BY6pE1fg.png" /></figure><h3>1. resso, React state management has never been so easy</h3><p>resso is a brand-new state manager for React, aims to provide the world’s easiest way to use.</p><p>resso also implements on-demand updating. If the unused data changed, it will never trigger the component updating.</p><p>GitHub: <a href="https://github.com/nanxiaobei/resso">https://github.com/nanxiaobei/resso</a></p><pre>import resso from &#39;resso&#39;;</pre><pre>const store = resso({ count: 0, text: &#39;hello&#39; });</pre><pre>function App() {<br>  const { count } = store; // deconstruct first, then use<br>  return (<br>    &lt;&gt;<br>      {count}<br>      &lt;button onClick={() =&gt; store.count++}&gt;+&lt;button&gt;<br>    &lt;&gt;<br>  );<br>}</pre><p>Only one API resso, just wrap the store object, nothing else.</p><p>To update, just re-assign the key of the store.</p><h3>2. How does React state manager works?</h3><p>Suppose there is a store, injected into different components:</p><pre>let store = {<br>  count: 0,<br>  text: &#39;hello&#39;,<br>};<br><br><em>// Component A<br></em>const { count } = store;<br>const [, updateA] = useState();<br><br><em>// Component B<br></em>const { text } = store;<br>const [, updateB] = useState();</pre><pre>// init<br>const listeners = [updateA, updateB];</pre><pre>// update<br>store = { ...store, count: 1 };<br>listeners.forEach((update) =&gt; update(store));</pre><p>Put the setState from each component into an array listeners.</p><p>When updating the store, iterate through listeners to call every setState, this will update every components.</p><p>How to monitor the store data changing? A public update function (such as Redux’s dispatch) can be provided, which is updated if called. You can also use the proxy&#39;s setter to listen.</p><p>Yes, almost all state managers work this way, it’s that simple. For example, the source code of Redux: <a href="https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L305-L308">https://github.com/reduxjs/redux/blob/master/src/createStore.ts#L305-L308</a></p><h3>3. How to optimize the update performance?</h3><p>All setState in listeners is called every time the store is updated, which can cause performance issues.</p><p>For example, when updating count, theoretically only A is expected to update, at this time B is also updated, but it does not use count at all.</p><p>How to update on demand? You can use a selector (such as Redux’s useSelector, or zustand&#39;s implementation):</p><pre><em>// Component A<br></em>const { count } = store;<br>const [, updateA] = useState();<br><br>const selector = (store) =&gt; store.count;<br><br>const newUpdateA = (newStore) =&gt; {<br>  if (count !== selector(newStore)) {<br>    updateA(newStore);<br>  }<br>};</pre><p>The same way for other components, subscribing newUpdateA to listeners can achieve &quot;on-demand update&quot; of components.</p><p>Above functions can also be implemented using proxy’s getter, and the data “used” by the component can be known through the getter.</p><h3>4. How is resso implemented internally?</h3><p>In the above implementation, one setState is collected in each component. When updating the store, determine whether to update the component through data comparison.</p><p>resso uses a new idea, which is actually more in line with the primitive concept of Hooks:</p><pre>let store = {<br>  count: 0,<br>  text: &#39;hello&#39;,<br>};<br><br><em>// Component A<br></em>const [count, setACount] = useState(store.count);<br><br><em>// Component B<br></em>const [count, setBCount] = useState(store.count);<br>const [text, setBText] = useState(store.text);<br><br><em>// inti<br></em>const listenerMap = {<br>  count: [setACount, setBCount],<br>  text: [setBText],<br>};<br><br><em>// update<br></em>store = { ...store, count: 1 };<br>listenerMap.count.forEach((setCount) =&gt; setCount(store.count));</pre><p>Use useState to inject each data from store used in the component, while maintaining a list of updaters for each key in the store.</p><p>The number of setStates collected in each component corresponds to the store data used. Instead of just collecting one setState for the component updating.</p><p>When updating, there is no need to do data comparison, because the update unit is based on the “data” level, not based on the “component” level.</p><p>To update a certain data is to call the updater list of this data, not the updater list of the component. Primitive the entire store.</p><h3>5. How the API of resso is designed?</h3><p>The secret to designing an API: write the usage you want first, and then figure out how to implement it. What comes out of this must be the most intuitive.</p><p>resso also thought about the following API design at the beginning:</p><p><strong>1. Similar to valtio</strong></p><pre>const store = resso({ count: 0, text: &#39;hello&#39; });</pre><pre>const snap = useStore(store);<br>const { count, text } = snap; // get<br>store.count++; // set</pre><p>This is the standard usage of Hooks, with the disadvantage of adding an extra API useStore. And using snap when getting, using store when setting, makes people split. This is definitely not the &quot;simplest&quot; way.</p><p><strong>2. Similar to valtio/macro</strong></p><pre>const store = resso({ count: 0, text: &#39;hello&#39; });</pre><pre>useStore(store);<br>const { count, text } = store; // get<br>store.count++; // set</pre><p>This is also achievable and is the standard usage of Hooks. At this time, the main body of get and set is unified, but it is still necessary to add a useStore API. This thing is only for calling Hooks, what if the user forgets to write it?</p><p>And in practice, it is found that when using store in each component, you have to import two things, store and useStore, which is definitely not as simple as importing only one store, especially when it is used in many components, will be very troublesome.</p><p><strong>3. In order to import only one store</strong></p><pre>const store = resso({ count: 0, text: &#39;hello&#39; });</pre><pre>store.useStore();<br>const { count, text } = store; // get<br>store.count++; // set</pre><p>This is the last hope of a “legal” use of Hooks, just importing a store, but it still looks weird and unacceptable anyway.</p><p>If you try to design this API, you will find that if you want to directly update the store (requires import store), and want to deconstruct store data from Hooks (need to import one more Hook, get and set are from different sources), no matter what, the design will looks awkward.</p><p>For the ultimate simplicity, for the easiest way to use, resso finally embarked on this API design:</p><pre>const store = resso({ count: 0, text: &#39;hello&#39; });</pre><pre>const { count } = store; // get<br>store.count++; // set</pre><h3>6. How to use resso?</h3><p><strong>Get store</strong></p><p>Because the store data is injected into the component using useState, it needs to be destructure first (destructure means calling useState), destructure at the top level of the component (Hooks rules, cannot be written after if), and then use, otherwise there will be a React warning.</p><p><strong>Set store</strong></p><p>Assignment to the first level of the store data will trigger the update, and only the assignment of the first level will trigger the update.</p><pre>store.obj = { ...store.obj, num: 10 }; // ✅ trigger update</pre><pre>store.obj.num = 10; // ❌ does not trigger update (valtio supports this way)</pre><p>resso does not support the writing method like valtio, mainly due to the following considerations:</p><ol><li>It is need to traverse all the data deeply to proxy, and when updating the data, it needs to be proxy first, which will cause a certain performance loss. (resso only proxy store once at the initialization.)</li><li>Because all data are proxy, it is not friendly when printed in the Chrome console, which is a big problem. (resso does not because only the store is a proxy, and generally prints the data in the store.)</li><li>If the sub-data is destructured, such as obj, obj.num = 10 can also trigger the update, which will cause the data source to be opaque, and it is uncertain whether it comes from the store and whether the assignment triggers the update. (The main body of resso is always from the store, and the source is clear.)</li></ol><h3>7. Simple. Not chaos</h3><p>The above is the design concept of resso and some implementations of a React state manager.</p><p>At the end of the day, the React state manager is a tool, React is a tool, JS is a tool, programming is a tool, and the job itself is a tool.</p><p>The purpose of tools is to create, to create works that act on the real world, not the tools themselves.</p><p>So, why not make it simpler?</p><p>jQuery is to simplify the development of native JS, React is to simplify the development of jQuery, development is to simplify the process in the real world, the Internet is to simplify people’s communication path, work path, consumption path, the meaning of development is to simplify, the meaning of the Internet itself is simplification, and the value of the Internet lies in simplification.</p><p>So, why not make it simpler?</p><p>Chic. Not geek.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QgBAQ39Iir5wMIFyZMr31g.jpeg" /></figure><p>Simplicity is everything.</p><p>try try resso: <a href="https://github.com/nanxiaobei/resso">https://github.com/nanxiaobei/resso</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a3b1b0ccaa99" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>