<?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"><channel><title><![CDATA[<Code With Yannick/>]]></title><description><![CDATA[Javascript fullstack developer for over 5 years, I like to keep learning and experimenting with new technologies. Welcome to my coding journey, subscribe to the]]></description><link>https://code-with-yannick.com</link><generator>RSS for Node</generator><lastBuildDate>Thu, 15 Oct 2026 05:21:24 GMT</lastBuildDate><atom:link href="https://code-with-yannick.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[React global state management in 2022]]></title><description><![CDATA[Welcome to this new article 👋
According to a poll on my Twitter, most of you are interested in learning more about React state management in 2022. It took some time but I finally found the time to write it properly!
Today, I'm going to introduce you...]]></description><link>https://code-with-yannick.com/react-global-state-management-in-2022</link><guid isPermaLink="true">https://code-with-yannick.com/react-global-state-management-in-2022</guid><category><![CDATA[React]]></category><category><![CDATA[React Native]]></category><category><![CDATA[recoil]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Tue, 15 Mar 2022 10:21:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1647336090057/N5vBJL2L0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-welcome-to-this-new-article">Welcome to this new article 👋</h2>
<p>According to a poll on my <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a>, most of you are interested in learning more about React state management in 2022. It took some time but I finally found the time to write it properly!</p>
<p>Today, I'm going to introduce you <a target="_blank" href="https://recoiljs.org">Recoil</a>, a global state management library developed and maintained by Meta. </p>
<p>First, I will explain what Recoil is, why it is different from other common solutions and finally we'll build a small app to illustrate what we've seen (link to the github repo at the end).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647259618952/0ns4JFG2s.gif" alt="final app gif" /></p>
<blockquote>
<p>This article was written and published using <a target="_blank" href="https://xpost.netlify.app">Xpost</a>, a productivity tool for technical writer I'm currently building in public.</p>
</blockquote>
<p>Here we go!</p>
<h2 id="heading-why-recoil">Why Recoil</h2>
<p>A lot of solutions are available when it comes to handling global state management in React.</p>
<blockquote>
<p>Disclaimer: this tutorial is focused on client side state management but it also exists server side ones (like <a target="_blank" href="https://react-query.tanstack.com/">react-query</a> or <a target="_blank" href="https://swr.vercel.app/">SWR</a>) that we can talk another time!</p>
</blockquote>
<h3 id="heading-context-api">Context API</h3>
<p>The first one, which I have already spoken about <a target="_blank" href="https://code-with-yannick.com/how-to-manage-global-state-with-context">here</a>, is the built-in <strong>Context API</strong>. As I explained, this API is really powerful and is enough for small/medium projects. However, it is intended to share global data <strong><em>only</em></strong> because it will re-render every child component on change which may result in performance issues.</p>
<h3 id="heading-redux">Redux</h3>
<p>Another one could be <strong>Redux</strong>. It's a really well-known library for managing global state, it's a robust and reliable solution, that's why a lot of companies still use it at the moment. Nevertheless, it requires a lot of configuration and boilerplates code to work properly. Moreover, from my experience, the learning curve for juniors is quite sharp.</p>
<h3 id="heading-recoil">Recoil</h3>
<p><strong>Recoil</strong> is (at the moment) less known but is the most promising way for the upcoming years. It offers to manage the global state at the atomic level (you will understand what does it means in a second).</p>
<p>Recoil made global state management way easier to maintain by drastically reducing boilerplates and configuration. </p>
<p>Unlike other solutions, it allows to keep the state as close as possible to the subscribers (components) thus avoiding unnecessary re-rendering and improving performance.</p>
<p>Moreover, Recoil provides a react-like syntax and behavior, the learning curve is therefore pretty soft when you already know how to use hook like <code>useState</code>.</p>
<p>Last but not least, because Recoil is developed and maintained by Meta it supports latest React features like concurrent mode, suspense, error boundaries... and works also with React Native!</p>
<blockquote>
<p>There are other libraries like <a target="_blank" href="https://jotai.org">Jotai</a> that share the same concept.</p>
</blockquote>
<h2 id="heading-when-to-use">When to use</h2>
<p>As for each library installed in your project, Recoil is an additional dependency, you need then to make sure it meets your needs (complexity, scalability, performance) before considering using it.</p>
<p>But you may ask yourself "how to know if it meets my needs ?" ?</p>
<p>In my case, as soon as a project get big enough (lots of components) and complex (multiple contexts, heavy component dependencies etc), I ask myself "would using Recoil help me improve the scalability and performance of the code base ?". If so, I consider implementing it.</p>
<h2 id="heading-recoil-concept">Recoil concept</h2>
<p>First of all, let's define some Recoil concept that will help you to follow along with this tutorial:</p>
<h3 id="heading-atom">Atom</h3>
<p>An <strong>Atom</strong> is a piece of global state that can be updated and subscribed by components. To be defined, an atom only needs a <strong><em>unique</em></strong> <code>key</code> and a <code>default</code> value. When a component subscribes to an atom it will be automatically re-render if the value changes.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// create atom</span>
<span class="hljs-keyword">const</span> nameState = atom({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'nameState'</span>, <span class="hljs-comment">// mandatory, must be unique</span>
  <span class="hljs-attr">default</span>: <span class="hljs-literal">null</span>, <span class="hljs-comment">// mandatory default value</span>
  <span class="hljs-attr">effects</span>: [   <span class="hljs-comment">// optional array of effects similar to useEffect ]</span>
});

<span class="hljs-comment">// then use it in components with hooks:</span>

<span class="hljs-comment">// get value and setter</span>
<span class="hljs-keyword">const</span> [name, setName] = useRecoilState(nameState);
<span class="hljs-comment">// get only value</span>
<span class="hljs-keyword">const</span> name = useRecoilValue(nameState);
<span class="hljs-comment">// get only setter</span>
<span class="hljs-keyword">const</span> setName = useSetRecoilState(nameState);
</code></pre>
<h3 id="heading-selector">Selector</h3>
<p>A <strong>Selector</strong> is a pure function that can be used to derive atoms and other selectors (synchronously or asynchronously). The selector is automatically refreshed on each change of atom or selector consumed. They can then be used (just like atoms) in components.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// create selector</span>
<span class="hljs-keyword">const</span> charCountSelector = selector({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'charCountSelector'</span>, <span class="hljs-comment">// mandatory &amp; unique</span>
  <span class="hljs-attr">get</span>: <span class="hljs-function">(<span class="hljs-params">{get}</span>) =&gt;</span> {
    <span class="hljs-comment">// consume other atom or selector</span>
    <span class="hljs-comment">// if nameState change selector will be refreshed automatically</span>
    <span class="hljs-comment">// and it's subscribers too</span>
    <span class="hljs-keyword">const</span> name = get(nameState);
    <span class="hljs-keyword">return</span> name.length;
  },
});

<span class="hljs-comment">// use it in components</span>
<span class="hljs-keyword">const</span> count = useRecoilValue(charCountSelector);
</code></pre>
<h3 id="heading-data-flow-graph">Data flow graph</h3>
<p>One of the most interesting things here is when you start building with atoms and selectors you get a scalable <a target="_blank" href="https://recoiljs.org/docs/guides/asynchronous-data-queries#data-flow-graph">data graph</a> that will automatically update subscribers (selectors &amp; components) on state change.</p>
<p>Thanks to these data flow it is pretty easy to build reactive apps. Let's imagine we have an atom corresponding to a selected item and a selector responsible for getting latest selected item details. Using Recoil, updating the atom will automatically trigger the selector (request the selected item details) and then notify the subscriber (UI component) to re-render.</p>
<p>Looks like magic right ? 😅</p>
<p>Enough talk, let's see how to implement it in a new app!</p>
<h2 id="heading-implementation">Implementation</h2>
<blockquote>
<p>Disclaimer: I will focus on explaining the Recoil part of the app if you are interested by the style, or simply want to check the code <a target="_blank" href="https://github.com/dpnick/recoil-demo">here is the link</a> to the repo. Feel free to send me a message on <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a> if you any questions / comments.</p>
</blockquote>
<p>As you can see below, our app will contain a list of users, clicking on a user card will select them, get their information and retrieve their blog posts from the API. Before being displayed their posts will then be filtered by category to appear in a bar chart.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647259618952/0ns4JFG2s.gif" alt="final app gif" /></p>
<h3 id="heading-configure-recoil">Configure Recoil</h3>
<p>Configuring Recoil is really easy it only requires two steps.
First, let's install the library:</p>
<pre><code class="lang-js">npm install recoil
<span class="hljs-comment">// or if you use yarn</span>
yarn add recoil
</code></pre>
<p>Then, simply wrap your app in a <code>RecoilRoot</code> provider</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// RecoilRoot needs to wrap all component using atoms and selectors</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">RecoilRoot</span>&gt;</span>
      ...
    <span class="hljs-tag">&lt;/<span class="hljs-name">RecoilRoot</span>&gt;</span></span>
  );
}
</code></pre>
<p>And that's it! You're now able to create atoms and selectors! ⚡️</p>
<blockquote>
<p>💡 Pro-tips: a good practice here is to use an <a target="_blank" href="https://reactjs.org/docs/error-boundaries.html#gatsby-focus-wrapper">ErrorBoundary</a> and <a target="_blank" href="https://reactjs.org/docs/react-api.html#reactsuspense">Suspense</a> components to handle both loading and error states that could happen in your <code>RecoilRoot</code>. Here is my final version:</p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ErrorBoundary</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">RecoilRoot</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Suspense</span>
          <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>
            &lt;<span class="hljs-attr">Fallback</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Spinner</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">Fallback</span>&gt;</span>
          }
        &gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Home</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Suspense</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">RecoilRoot</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ErrorBoundary</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-architecture">Architecture</h3>
<p>Recoil doesn't impose architecture or naming convention. As you can see, in my case, I like to keep all my Recoil-related stuff into a <code>store</code> folder. Inside this folder, atoms and selectors are separated in folders respectively named.</p>
<blockquote>
<p>💡 You could also choose to keep your atoms and selectors separated by concern (for ex. all user related stuff in a folder). Once again, it depends of your needs.</p>
</blockquote>
<p>Finally, I like to suffix atoms and selectors with their type (for ex. <code>nameAtom.ts</code> and <code>charCountSelector</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1647271337233/bNUPbkiR4.png" alt="final app architecture" /></p>
<h3 id="heading-list-of-users">List of users</h3>
<p>For our use case, we will need two atoms. The first one will contain the list of users and the second the <code>id</code> of the selected one.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/store/atoms/userListAtom.ts</span>
<span class="hljs-keyword">import</span> { atom } <span class="hljs-keyword">from</span> <span class="hljs-string">'recoil'</span>;
<span class="hljs-keyword">import</span> { getUsers } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../lib/jsonPlaceholder'</span>;
<span class="hljs-keyword">import</span> { User } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../models/User'</span>;

<span class="hljs-keyword">const</span> userListAtom = atom&lt;User[]&gt;({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'userListAtom'</span>,
  <span class="hljs-comment">// get users from API and use it as default value</span>
  <span class="hljs-attr">default</span>: getUsers(),
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> userListAtom;
</code></pre>
<p>This first atom is simple, we just defined a key and a default value to be our list of users.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/store/atoms/selectedUserIdAtom.ts</span>
<span class="hljs-keyword">import</span> { atom } <span class="hljs-keyword">from</span> <span class="hljs-string">'recoil'</span>;

<span class="hljs-keyword">const</span> selectedUserIdAtom = atom&lt;number | <span class="hljs-literal">null</span>&gt;({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'selectedUserIdAtom'</span>,
  <span class="hljs-attr">default</span>: <span class="hljs-literal">null</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> selectedUserIdAtom;
</code></pre>
<p>This second atom is similar, excepted that we defined our default value to <code>null</code> because no user is selected at first.</p>
<p>Once defined we can directly use these atoms in our <code>UserList</code> component. This component will display each user in a card, a click on card will update our <code>selectedUserIdAtom</code>.</p>
<blockquote>
<p>UI components (<code>List</code> &amp; <code>ClickableCard</code>) are available in the repo.</p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/components/UserInfo.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserList</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> users = useRecoilValue(userListAtom);
  <span class="hljs-keyword">const</span> [userId, setUserId] = useRecoilState(selectedUserIdAtom);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">List</span>&gt;</span>
      {users.map(({ id, name }) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">ClickableCard</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{id}</span>
          <span class="hljs-attr">isSelected</span>=<span class="hljs-string">{id</span> === <span class="hljs-string">userId}</span>
          <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setUserId(id)}
        &gt;
          {name}
        <span class="hljs-tag">&lt;/<span class="hljs-name">ClickableCard</span>&gt;</span>
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">List</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now that we have our working user list let's get the infos and posts for the selected user!</p>
<h3 id="heading-selected-user-details">Selected user details</h3>
<p>To do so, we we will need 3 selectors. Let's start by retrieving selected user informations.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> selectedUserSelector = selector({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'selectedUserSelector'</span>,
  <span class="hljs-attr">get</span>: <span class="hljs-function">(<span class="hljs-params">{ get }</span>) =&gt;</span> {
    <span class="hljs-comment">// subscribe to our atoms</span>
    <span class="hljs-comment">// selector will be updated if any of the two change</span>
    <span class="hljs-keyword">const</span> users: User[] = get(userListAtom);
    <span class="hljs-keyword">const</span> selectedId = get(selectedUserIdAtom);

    <span class="hljs-keyword">const</span> result = users.find(<span class="hljs-function">(<span class="hljs-params">{ id }</span>) =&gt;</span> id === selectedId);
    <span class="hljs-keyword">return</span> result;
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> selectedUserSelector;
</code></pre>
<p>This first selector will only be responsible for deriving our atoms. If found, it returns the selected user.</p>
<p>You can try it by adding this to the <code>UserInfo</code> component</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/components/UserInfo.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserInfo</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> selectedUser = useRecoilValue(selectedUserSelector);

  <span class="hljs-keyword">if</span> (selectedUser) {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CenteredCard</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>Selected user details<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>name: {selectedUser.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>email: {selectedUser.email}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>company: {selectedUser.company.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">CenteredCard</span>&gt;</span></span>
    );
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}
</code></pre>
<p>Now, we need to get the posts for our selected user. A common pattern to fetch these data in React would be to use a <code>useEffect</code> hook.
However, using Recoil we can delegate this task to another selector:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/store/selectors/postByUserSelector.ts</span>
<span class="hljs-keyword">const</span> postByUserSelector = selector({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'postByUserSelector'</span>,
  <span class="hljs-attr">get</span>: <span class="hljs-keyword">async</span> ({ get }) =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-comment">// refresh each time we select a new user id</span>
      <span class="hljs-keyword">const</span> selectedId = get(selectedUserIdAtom);
      <span class="hljs-keyword">let</span> result: Post[] | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
      <span class="hljs-keyword">if</span> (selectedId) {
        <span class="hljs-comment">// if a user is selected =&gt; get posts for this id</span>
        result = <span class="hljs-keyword">await</span> getPostsByUser(selectedId);
      }
      <span class="hljs-keyword">return</span> result;
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-keyword">throw</span> error;
    }
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> postByUserSelector;
</code></pre>
<p>This selector will be automatically called every time a new user is selected. By subscribing to it, it ensures we will have the posts for the latest user selected.</p>
<p>We need now to transform this output (array of posts) to a dataset for our chart (number of post per category). We could have implemented it in this selector, but it might be useful to be able to access "raw" posts data so we are going to create a last new selector.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/store/selectors/postByCategorySelector.ts</span>
<span class="hljs-keyword">const</span> postByCategorySelector = selector({
  <span class="hljs-attr">key</span>: <span class="hljs-string">'postByCategorySelector'</span>,
  <span class="hljs-attr">get</span>: <span class="hljs-function">(<span class="hljs-params">{ get }</span>) =&gt;</span> {
    <span class="hljs-comment">// subscribe to our previous selector</span>
    <span class="hljs-keyword">const</span> posts = get(postByUserSelector);

    <span class="hljs-comment">// generate array of number</span>
    <span class="hljs-keyword">const</span> result = fakeCategories.map(<span class="hljs-function">(<span class="hljs-params">category</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> selectedPosts = posts?.filter(<span class="hljs-function">(<span class="hljs-params">post</span>) =&gt;</span> post.category === category);
      <span class="hljs-keyword">return</span> selectedPosts?.length ?? <span class="hljs-number">0</span>;
    });
    <span class="hljs-keyword">return</span> result;
  },
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> postByCategorySelector;
</code></pre>
<p>Next, we need to subscribe to this selector in our <code>BarChart</code> component</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// ./src/components/BarChart.tsx</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">BarChart</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> values = useRecoilValue(postByCategorySelector);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ChartContainer</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ReactFrappeChart</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">'bar'</span>
        <span class="hljs-attr">colors</span>=<span class="hljs-string">{[</span>'#<span class="hljs-attr">273E89</span>']}
        <span class="hljs-attr">axisOptions</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">xAxisMode:</span> '<span class="hljs-attr">tick</span>', <span class="hljs-attr">yAxisMode:</span> '<span class="hljs-attr">tick</span>', <span class="hljs-attr">xIsSeries:</span> <span class="hljs-attr">1</span> }}
        <span class="hljs-attr">height</span>=<span class="hljs-string">{250}</span>
        <span class="hljs-attr">data</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">labels:</span> <span class="hljs-attr">fakeCategories</span>,
          <span class="hljs-attr">datasets:</span> [{ <span class="hljs-attr">values</span> }],
        }}
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ChartContainer</span>&gt;</span></span>
  );
}
</code></pre>
<p>And then, add it in <code>UserInfo</code> as follow</p>
<pre><code class="lang-diff">// ./src/components/UserInfo.tsx
export default function UserInfo() {
  const selectedUser = useRecoilValue(selectedUserSelector);

  if (selectedUser) {
    return (
      &lt;CenteredCard&gt;
        &lt;h3&gt;Selected user details&lt;/h3&gt;
        &lt;span&gt;name: {selectedUser.name}&lt;/span&gt;
        &lt;span&gt;email: {selectedUser.email}&lt;/span&gt;
        &lt;span&gt;company: {selectedUser.company.name}&lt;/span&gt;
<span class="hljs-addition">+        &lt;Suspense</span>
<span class="hljs-addition">+          fallback={</span>
<span class="hljs-addition">+            &lt;Fallback&gt;</span>
<span class="hljs-addition">+              &lt;Spinner /&gt;</span>
<span class="hljs-addition">+            &lt;/Fallback&gt;</span>
<span class="hljs-addition">+          }</span>
<span class="hljs-addition">+        &gt;</span>
<span class="hljs-addition">+          &lt;BarChart /&gt;</span>
<span class="hljs-addition">+        &lt;/Suspense&gt;</span>
      &lt;/CenteredCard&gt;
    );
  }
  return null;
}
</code></pre>
<p>As you can see, we are using here another <code>Suspense</code> to wrap our <code>BarChart</code> component. Since <code>BarChart</code> subscribe to an async action (getting posts from the API endpoint) adding a <code>Suspense</code> here allow to manage its pending state separately from the rest of the app.</p>
<blockquote>
<p>💡 Thanks to this <code>Suspense</code> we are able to display a spinner instead of the chart while fetching posts.
Without it, <code>BarChart</code> would use the nearest <code>Suspense</code> (the one in <code>App.tsx</code> in our case) and therefore hide every components while loading (which results in a very bad user experience).</p>
</blockquote>
<p>Finally, use our components in the <code>Home</code> page</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">HomeContainer</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">RecoilLogo</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UserList</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UserInfo</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">HomeContainer</span>&gt;</span></span>
  );
}
</code></pre>
<p>Congrats! You made it through this entire tutorial! 🎉</p>
<h2 id="heading-conclusion-andamp-going-further">Conclusion &amp; going further</h2>
<p>After this tutorial, you should now have a good understanding of the basics of Recoil. If you want to go further I suggest you to start by visiting the <a target="_blank" href="https://recoiljs.org/fr/docs/guides/asynchronous-data-queries">Recoil documentation website</a> and more especially to check features like:</p>
<ul>
<li>atomFamily &amp; selectorFamily</li>
<li>concurrent requests (waitForAll, noWait...)</li>
<li>persistence</li>
</ul>
<p>I hope you learned something useful, if you have any questions/suggestions do not hesitate to join me on <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a> I will be happy to talk with you all.</p>
<p>See you soon! 👋</p>
<p>- Yannick</p>
<ul>
<li><a target="_blank" href="https://github.com/dpnick/recoil-demo">Github repo</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Xpost: productivity tool for content creator & technical writer]]></title><description><![CDATA[Hi friends, welcome to this new article 👋
As you may have guess, Today’s article will be a little bit “special”, we are going to talk about the project I submitted to the Hashnode  x Netlify hackathon!
Disclaimer, here are the links to the live vers...]]></description><link>https://code-with-yannick.com/xpost-productivity-tool</link><guid isPermaLink="true">https://code-with-yannick.com/xpost-productivity-tool</guid><category><![CDATA[Developer]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Netlify]]></category><category><![CDATA[NetlifyHackathon]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Tue, 01 Mar 2022 00:13:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1646126756589/fn6EHB8XW.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-hi-friends-welcome-to-this-new-article">Hi friends, welcome to this new article 👋</h1>
<p>As you may have guess, Today’s article will be a little bit “special”, we are going to talk about the project I submitted to the Hashnode  x Netlify hackathon!</p>
<p>Disclaimer, here are the links to the live version and the code:</p>
<ul>
<li><a target="_blank" href="https://xpost.netlify.app/">Live version</a></li>
<li><a target="_blank" href="https://github.com/dpnick/xpost">GitHub repo</a></li>
</ul>
<p>Oh and other disclaimer, I wrote this blog post using the tool I'm going to introduce 😄</p>
<h2 id="heading-the-problem">The problem</h2>
<p>First of all, let’s talk about the problem I tried to address. As technical writer and / or content creator we are often using multiple platforms to share our articles. This is called "cross-posting" and it allows (if it is well executed) to generate more traffic on your content.</p>
<p>This looks pretty straight right ?</p>
<p>However, it is not as easy for two main reasons:</p>
<ul>
<li>In order to keep your SEO as efficient as possible you have to define the original URL of your article (where it was initially published) aka canonical URL. This canonical URL must be passed to every other platform to let the search engine know it is not a duplicate.</li>
<li>Once you published your blog post on a first platform and get your canonical URL you have to copy-paste your article on every other platforms you want to publish. It is a very repetitive and boring task.</li>
</ul>
<h2 id="heading-idea-andamp-features">Idea &amp; features</h2>
<p>To address this problem, my idea was to create a “hub”, where every content creator get a personal dashboard, the ability to connect their favorite platforms and to create articles thanks to a consistent writing editor. Once a draft is ready to be published, you only have to click a button to directly send it to one or multiple of your blogging platforms without worrying about anything.</p>
<h3 id="heading-article-editor">Article editor</h3>
<p>I started by focusing on the text editor because that’s where we all spend the most time. I wanted to create a nice writing experience so I chose a Notion-like editor. As the image show bellow, it is a markdown editor with instant rendering and shortcut commands (using <code>/</code>). This is (in my opinion) the best compromise between comfort and efficiency.</p>
<p>To make it more convenient I also added an <strong>auto-save</strong> feature so you don’t have to worry about forgetting.</p>
<p>It is also possible to add <strong>images</strong> (through a command or by dropping it), <strong>embedded videos</strong> (youtube).</p>
<p>Finally, just like Notion, you can <strong>highlight text</strong> to apply styling (bold, line through, etc).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646144923837/eXKOzSC8D.gif" alt="Article editor GIF" /></p>
<h3 id="heading-dashboard">Dashboard</h3>
<p>Concerning the dashboard, the idea was to allow the user to <strong>manage his blogging platforms</strong>, get <strong>useful insight</strong> but most importantly to <strong>find his drafts and published articles</strong>.</p>
<p>I also added some quick actions. It is then possible to <strong>publish / delete a draft</strong> and <strong>visit a published article</strong> from the dashboard.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646145660675/7YwUIB8lx.gif" alt="Dashboard GIF" /></p>
<p>I tried to focus on the UI/UX to make each step as easy as possible for the user. You can see below the flow to add a new platform:</p>
<p> <img src="http://res.cloudinary.com/dpnick/image/upload/v1646091427/ulhoiskbklad7zgsxa7u.png" alt="Add a new provider" /></p>
<p>After choosing a platform, instructions are shown in a modal to explain to the user where to find the requested informations. He then only have to confirm to get ready.</p>
<p>The publication flow is quite similar:</p>
<p> <img src="http://res.cloudinary.com/dpnick/image/upload/v1646091471/jiuvs7w1z3ojbamegw9c.png" alt="Publish a draft" /></p>
<p>After clicking “Publish”, a modal appear, the user must choose one or multiple platforms to publish his article. If more than one are selected, he can choose which one to use as original URL thanks to a simple dropdown. Once published the user has the ability to share the original URL on Twitter.
That's it, all the hard work is automatically handled on our side.</p>
<h3 id="heading-responsiveness">Responsiveness</h3>
<p>Of course, I made sure that XPost was fully usable on mobile as you can see here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646155008533/K2E1ZxE2H.gif" alt="mobile GIF" /></p>
<h3 id="heading-landing-page">Landing page</h3>
<p>Finally, I created a landing page to introduce XPost to new users. This is a simple page which quickly presents how it works and why they should use it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1646156018506/JIvJRjATl.gif" alt="Landing page GIF" /></p>
<h2 id="heading-tech">Tech</h2>
<h3 id="heading-stack">Stack</h3>
<p>This project required multiple technologies that i’m going to introduce:</p>
<ul>
<li>Next.js (typescript) for front-end and API</li>
<li>styled-components &amp; styled-system for custom UI components</li>
<li>Netlify for hosting and serverless functions</li>
<li>Next-auth for authentification</li>
<li>planetscale &amp; prisma for database</li>
<li>cloudinary for images</li>
<li>Hashnode &amp; dev.to APIs</li>
</ul>
<h3 id="heading-coding-practices">Coding practices</h3>
<ul>
<li>Fully typed using Typescript</li>
<li>Strict ESLint configuration</li>
<li>Husky to run types and linter before committing </li>
<li>Local Prettier to format code</li>
<li>App architecture (splitting business logic and UI)</li>
<li>data fetching best practices (SWR, caching, static props)</li>
<li>Generic &amp; reusable UI components </li>
<li>SEO ready</li>
</ul>
<p>My code is available <a target="_blank" href="https://github.com/dpnick/xpost">here</a></p>
<h2 id="heading-what-i-learned">What I learned</h2>
<p>I really like hackathons to challenge myself on new stacks. This time it was the opportunity to work with Netlify, Planetscale and Prisma. I have to say, these are all amazing tools to work with, they greatly improved my efficiency during this hackathon.</p>
<p>Also, even for an experienced front-end developer like me, it's always nice to be able to add new skills and products to my portfolio 🙂</p>
<h2 id="heading-what-to-expect-next">What to expect next</h2>
<p>This is just the beginning for this project, I planned to add a lot of new features like:</p>
<ul>
<li>more blogging platforms</li>
<li>scheduling article (almost ready)</li>
<li>calendar view</li>
<li>integrate other content creator tools (like communication)</li>
</ul>
<p>If you are interested in this project, you can send me a message on <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a> ! 🙂</p>
<p>Thanks you so much for reading, I hope you enjoyed it as much as I do! Feel free to leave a comment if you have any questions!</p>
<p>See you soon!</p>
<p>Yannick 👋</p>
]]></content:encoded></item><item><title><![CDATA[Customizing your ZSH terminal in less than 5minutes]]></title><description><![CDATA[Hey friends, welcome to this quick tutorial 👋
Today, I'm gonna show you how I personally set up my ZSH terminal on my mac. At the end of this tutorial, you will be able to configure it according to your own needs/preferences.
Let's get started!
iTer...]]></description><link>https://code-with-yannick.com/customizing-your-zsh-terminal-in-less-than-5minutes</link><guid isPermaLink="true">https://code-with-yannick.com/customizing-your-zsh-terminal-in-less-than-5minutes</guid><category><![CDATA[zsh]]></category><category><![CDATA[terminal]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Tue, 25 Jan 2022 05:18:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1643024018621/OKI33bGrh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-hey-friends-welcome-to-this-quick-tutorial">Hey friends, welcome to this quick tutorial 👋</h1>
<p>Today, I'm gonna show you how I personally set up my ZSH terminal on my mac. At the end of this tutorial, you will be able to configure it according to your own needs/preferences.</p>
<p>Let's get started!</p>
<h2 id="heading-iterm-2">iTerm 2</h2>
<p><a target="_blank" href="https://iterm2.com/">iTerm 2</a> is a replacement for the default mac terminal app. It is totally free and brings a lot of very convenient features (like split panes, global search, triggers...) as well as multiple customization possibilities (we will see it later).</p>
<p>Start by downloading the app <a target="_blank" href="https://iterm2.com/downloads.html">here</a> and install it on your mac.</p>
<h2 id="heading-configuration">Configuration</h2>
<p><a target="_blank" href="https://ohmyz.sh/">Oh-my-zsh</a> is an open-source framework that will help you managing your zsh configuration. It allows you to use built-in helpers, themes, plugins...</p>
<p>Install it using the following curl command:</p>
<pre><code class="lang-console">sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
</code></pre>
<p>By default, oh-my-zsh comes bundled with the <code>git</code> plugin. It means you can use all <a target="_blank" href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/git">these aliases</a>. If you want to try, go to a git repository and run <code>gst</code> to get your current git status.</p>
<p>To add another plugin, choose one from <a target="_blank" href="https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins">this list</a>. Then, in your .zshrc (located at your <code>~</code>), find the line containing <code>plugins=(git)</code> and add the name of your newly selected plugin to the <code>plugins</code> array. Finally, do not forget to restart your terminal.</p>
<p>For example to add <code>z</code> and GitHub cli:</p>
<pre><code class="lang-console">plugins=(
  # other plugins...
  git z gh
)
</code></pre>
<blockquote>
<p><a target="_blank" href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/z"><code>z</code></a> is one of my favorite plugins, it allows you to easily navigate to your most visited directories.</p>
</blockquote>
<h3 id="heading-adding-auto-complete-optional">Adding auto-complete (optional)</h3>
<p>It is also possible to easily add command autocomplete based on your history. I personally use it so I'm gonna share it with you.</p>
<p>First, go to <code>~/.oh-my-zsh/custom/plugins</code> and clone this repository:</p>
<pre><code class="lang-console">git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions
</code></pre>
<p>Then go back to your <code>~/.zshrc</code> and add <code>zsh-autosuggestions</code> to the array like:</p>
<pre><code class="lang-console">plugins=( 
    # other plugins...
    zsh-autosuggestions
)
</code></pre>
<p>Restart your <code>iTerm</code> and you are done! Try navigating around, you should now have nice suggestions 🎉</p>
<h2 id="heading-theme">Theme</h2>
<p><a target="_blank" href="https://github.com/romkatv/powerlevel10k">Powerlevel10k</a> allows you to highly customize your terminal theme using a "configuration wizard". This wizard will ask for your preferences and create the corresponding theme configuration.</p>
<p>Go ahead and install it:</p>
<pre><code class="lang-console">git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k
echo 'source ~/powerlevel10k/powerlevel10k.zsh-theme' &gt;&gt;~/.zshrc
</code></pre>
<p>Then restart <code>iTerm</code>. At the opening, you should access the <code>powerlevel10k</code> wizard.</p>
<p>At first, it can ask you to install a few fonts, install them to access all customization properties.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642947795895/9_sPu9Vj2.png" alt="Capture d’écran 2022-01-23 à 15.21.17.png" /></p>
<p>Once done, stop <code>iTerm</code> by using the shortcut <code>⌘Q</code> and restart it. It should, then, ask you about your preferences as shown in the following video:</p>
<blockquote>
<p>If the configuration wizard doesn't start automatically, use the command <code>p10k configure</code></p>
</blockquote>
<p>💡 Enable English subtitles if you want to see my choices</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/s9ms2K-7aG4">https://youtu.be/s9ms2K-7aG4</a></div>
<h2 id="heading-iterm-customization">iTerm Customization</h2>
<p>Open <code>iTerm</code> preferences. Here you will be able to customize your terminal colors, opacity, cursor, widgets, and much more.</p>
<p>Let's start by finding the right colors under <code>Profiles &gt; Colors</code>. You can either directly choose your own colors or use a preset. To find a preset that suits you go to the <a target="_blank" href="https://iterm2colorschemes.com/">iterm2colorschemes</a> site.</p>
<blockquote>
<p>To export a preset, open it and export it with <code>save page as</code>.  The exported file will have the <code>.itermcolors.txt</code> extensions, rename it to keep only <code>.itermcolors</code>. Then import this file in <code>iTerm</code> to be able to use it in the <code>color presets</code> dropdown.</p>
</blockquote>
<p>💡 At this time, my favorite color scheme is <code>Guezwhoz</code>. Do not hesitate to share yours with me in the comments below! You will maybe find my next favorite one 😄</p>
<p>In the following video, I show you how I changed:</p>
<ul>
<li><strong>colors</strong>: to use an imported scheme</li>
<li><strong>cursor</strong>: to use blinking underscore</li>
<li><strong>background</strong>: to make it a little bit transparent (I like being able to see what's behind the terminal)</li>
<li><strong>widgets</strong>: to add some useful informations</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/MfLN-wFiPWo">https://youtu.be/MfLN-wFiPWo</a></div>
<p>💡 Just forgot to show one thing: in <code>Colors</code> I set <code>Minimum Contrast</code> to <code>50</code> to prevent white text on light colors etc.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for following this tutorial until the end! I hope your new terminal configuration will help you to improve your productivity as it does for me. As always, if you have any questions/suggestions do not hesitate to join me on <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a> I will be happy to talk with you all.</p>
<p>🙋‍♂️ In this tutorial, I tried including videos to make it shorter and easier to follow. What did you think about it? Did you like it? </p>
<p>See you next time! 👋</p>
<p>- Yannick</p>
]]></content:encoded></item><item><title><![CDATA[How to smoothly animate your react native app]]></title><description><![CDATA[Wecome to the last post of this series 👋
What are we going to build
In the today's blog, as promised, I will show you how to give a little more peps to our note-taking app ! 

As always, if you haven't already, I highly recommend you to read the two...]]></description><link>https://code-with-yannick.com/how-to-animate-react-native-app</link><guid isPermaLink="true">https://code-with-yannick.com/how-to-animate-react-native-app</guid><category><![CDATA[Expo]]></category><category><![CDATA[React Native]]></category><category><![CDATA[animation]]></category><category><![CDATA[user experience]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Mon, 10 Jan 2022 10:35:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1641764437942/TPKA4QlOB_.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-wecome-to-the-last-post-of-this-series">Wecome to the last post of this series 👋</h1>
<h2 id="heading-what-are-we-going-to-build">What are we going to build</h2>
<p>In the today's blog, as promised, I will show you how to give a little more peps to our note-taking app ! </p>
<blockquote>
<p>As always, if you haven't already, I highly recommend you to read the two other posts of this series first to get all the context. I will start from where we left off last time.</p>
</blockquote>
<p>This is what we are going to build in this tutorial :</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/ODYR60GWg2I">https://youtu.be/ODYR60GWg2I</a></div>
<p>You may have noticed, there are four main animations to take care of :</p>
<ul>
<li>list cards (layout animation)</li>
<li>scroll list</li>
<li>header content</li>
<li>theme</li>
</ul>
<h2 id="heading-animation-disclaimer">Animation disclaimer</h2>
<p>Nowadays, animations are really important tools that make your app unique and provide your users a feeling of satisfaction. On the other hand, too much or poorly implemented animations will give your app a cheap and useless feeling.</p>
<p>That's why, as app developer you need to pay attention to several things :</p>
<ul>
<li>don't over-use animations, especially complex ones, from my point of view micro-interactions are the keys to offer your users the best experience.</li>
<li>your animations must serve your design / functionalities and not the other way around.</li>
<li>make sure your animations run smoothly.</li>
</ul>
<p>React native comes with a built-in library called <a target="_blank" href="https://reactnative.dev/docs/animated">animated</a>. This library is really nice while animating non-layout properties like <code>opacity</code>, <code>scale</code> and others which can use native driver.</p>
<p>However, when it comes to animate properties like <code>width</code> or <code>padding</code>, the animated API suffers from serious performance issues. The main reason for this is : non-native-driver animation are defined on the JS thread and need to use a bridge to reach the UI thread. You surely guessed it, native-driver animation, directly use the UI thread and are, consequently, way more performant.</p>
<blockquote>
<p>If you want more informations about how Animated API works and its caveats I recommend you to read this <a target="_blank" href="https://reactnative.dev/blog/2017/02/14/using-native-driver-for-animated">article</a></p>
</blockquote>
<p>This is where <a target="_blank" href="https://docs.swmansion.com/react-native-reanimated/"><code>react-native-reanimated@2</code></a> from <a target="_blank" href="https://swmansion.com/">Software Mansion</a> comes up.</p>
<h2 id="heading-reanimated-2">Reanimated 2</h2>
<p>To keep it simple this (wonderful) library provides a way to declare your animation in JS but to have them all running on the UI thread. This means a huge performance increase.</p>
<p>It also provide a really nice API to manage gesture events, layout animations, ...</p>
<blockquote>
<p>Reanimated 2 is available for expo since the SDK 44 only, before continuing make sure your project is up to date with this version !</p>
</blockquote>
<h3 id="heading-installation">Installation</h3>
<p>Before starting we need to install and configure reanimated :</p>
<pre><code class="lang-bash">expo install react-native-reanimated
</code></pre>
<p>Then add the babel plugin :</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">api</span>) </span>{
  api.cache(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">presets</span>: [<span class="hljs-string">'babel-preset-expo'</span>],
    <span class="hljs-attr">plugins</span>: [
      <span class="hljs-string">'babel-plugin-styled-components'</span>,
      [
        <span class="hljs-string">'module-resolver'</span>,
        {
          <span class="hljs-attr">root</span>: [<span class="hljs-string">'./src'</span>],
          <span class="hljs-attr">extensions</span>: [<span class="hljs-string">'.ios.js'</span>, <span class="hljs-string">'.android.js'</span>, <span class="hljs-string">'.js'</span>, <span class="hljs-string">'.ts'</span>, <span class="hljs-string">'.tsx'</span>, <span class="hljs-string">'.json'</span>],
          <span class="hljs-attr">alias</span>: {
            <span class="hljs-string">'@components'</span>: <span class="hljs-string">'./src/components'</span>,
            <span class="hljs-string">'@navigation'</span>: <span class="hljs-string">'./src/navigation'</span>,
            <span class="hljs-string">'@contexts'</span>: <span class="hljs-string">'./src/contexts'</span>,
            <span class="hljs-string">'@models'</span>: <span class="hljs-string">'./src/models'</span>,
            <span class="hljs-string">'@hooks'</span>: <span class="hljs-string">'./src/hooks'</span>,
            <span class="hljs-string">'@pages'</span>: <span class="hljs-string">'./src/pages'</span>,
          },
        },
      ],
      <span class="hljs-string">'react-native-reanimated/plugin'</span>,
    ],
  };
};
</code></pre>
<blockquote>
<p>If you load other Babel plugins, the Reanimated plugin has to be the last item in the plugins array.</p>
</blockquote>
<p>Restart your app and clean the cache with the following :</p>
<pre><code class="lang-bash">expo start -c
</code></pre>
<p>Congrats you can now build amazing animations 🤪</p>
<h2 id="heading-card-layout-animation">Card layout animation</h2>
<p>Reanimated <a target="_blank" href="https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/layout_animations/"><code>layout animation</code></a> is a convenient way to provide component smooth entering / exiting / layout animations. It is especially useful if you have a filtrable / sortable list with moving items just like we do with our notes.</p>
<p>Go ahead and update your NoteCard component as bellow :</p>
<pre><code class="lang-diff">// src/components/NoteCard.tsx

<span class="hljs-addition">+ // animated component can either be created using this method</span>
<span class="hljs-addition">+ // or directly in your template like &lt;Animated.View&gt; </span>
<span class="hljs-addition">+const AnimatedStyledView = Animated.createAnimatedComponent(StyledView);</span>

export default function NoteCard({
  note,
  showNoteEdit,
  ...props
}: NoteCardProps) {
  const showDetail = () =&gt; showNoteEdit(note.id, note.title);

  return (
    &lt;Pressable
      onPress={showDetail}
      style={({ pressed }) =&gt; ({ opacity: pressed ? 0.7 : 1 })}
    &gt;
<span class="hljs-addition">+      &lt;AnimatedStyledView entering={FadeInUp} exiting={FadeOutDown} {...props}&gt;</span>
<span class="hljs-deletion">-      &lt;StyledView {...props}&gt;</span>
        &lt;StyledText
          color='white'
          numberOfLines={1}
          fontWeight='bold'
          fontSize={2}
        &gt;
          {note.title}
        &lt;/StyledText&gt;
        &lt;StyledText color='accent' mb={8}&gt;
          {formatDistanceToNow(note.updatedAt, {
            addSuffix: true,
          })}
        &lt;/StyledText&gt;
        &lt;StyledText color='whitesmoke' numberOfLines={8}&gt;
          {note.content}
        &lt;/StyledText&gt;
<span class="hljs-addition">+      &lt;/AnimatedStyledView&gt;</span>
<span class="hljs-deletion">-       &lt;/StyledView&gt;</span>
    &lt;/Pressable&gt;
  );
}
</code></pre>
<blockquote>
<p>Reanimated comes with a lot of predefined layout animation. For example you can replace <code>FadeInUp</code> with <code>BounceIn</code>. You can found a full list of animations <a target="_blank" href="https://docs.swmansion.com/react-native-reanimated/docs/api/LayoutAnimations/entryAnimations/#animations">here</a></p>
</blockquote>
<p>And this is all that it takes! Try your app, search some notes, you should know have a nice fade in up entering and fade out down exiting animations ! 🎉</p>
<h2 id="heading-scroll-animation">Scroll animation</h2>
<p>In case of the user having a lot of notes, we want him to be able to see them all easily. To do so, we'll use the scroll event from our <code>ScrollView</code> to make our header "shrinkable". At the same time we'll move our <code>SearchBar</code> component to be part of the header when in the "shrink" state.</p>
<p>First, let's define some layout variables we are going to use to build our animations. Create a new file <code>models/layout.ts</code> and paste the following content inside :</p>
<pre><code><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> SEARCH_BAR_HEIGHT = <span class="hljs-number">40</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> PADDING_TOP_BAR = <span class="hljs-number">16</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> HEADER_FULL_HEIGHT = <span class="hljs-number">300</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> HEADER_MIN_HEIGHT = <span class="hljs-number">100</span> + SEARCH_BAR_HEIGHT + PADDING_TOP_BAR;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> HEADER_SCROLL_DISTANCE = HEADER_FULL_HEIGHT - HEADER_MIN_HEIGHT;
</code></pre><blockquote>
<p>💡 To be able to test while creating your animation, make sure to have enough notes to scroll.</p>
</blockquote>
<h3 id="heading-home-page">Home page</h3>
<p>In this component, we are going to get the vertical content offset from the scroll event emitted by the <code>ScrollView</code>. Once done, we are going to provide it to other components that need it (Header and SearchBar).</p>
<p>(check code comments)</p>
<pre><code class="lang-diff">// src/pages/Home.tsx
// ...imports
const StyledScrollView = styled.ScrollView&lt;LayoutProps &amp; SpaceProps&gt;(
  compose(layout, space)
);

<span class="hljs-addition">+const AnimatedScrollView = Animated.createAnimatedComponent(StyledScrollView);</span>

export type HomeProps = NativeStackNavigationProp&lt;HomeStackParamList, 'Home'&gt;;

<span class="hljs-deletion">-const SEARCH_INPUT_HEIGHT = 40;</span>
<span class="hljs-deletion">-const PADDING_VERTICAL_SEARCH = 32;</span>

export default function Home() {
  const { notes } = useNote();
  const [isLoading, setIsLoading] = useState&lt;boolean&gt;(true);
  const [filteredNotes, setFilteredNotes] = useState&lt;Note[]&gt;([]);
  const [search, setSearch] = useState&lt;string&gt;('');
  const debouncedSearch = useDebounce&lt;string&gt;(search, 500);

  useEffect(() =&gt; {
    if (!notes) return;
    setFilteredNotes(notes);
    setSearch('');

    setTimeout(() =&gt; setIsLoading(false), 3000);
  }, [notes]);

  useEffect(() =&gt; {
    if (!notes) return;
    const nextData = notes.filter((note) =&gt;
      note.title.toLocaleLowerCase().includes(search.toLocaleLowerCase())
    );
    setFilteredNotes(nextData);
  }, [debouncedSearch]);

<span class="hljs-addition">+ // create a value that can be shared / used both on UI and JS threads</span>
<span class="hljs-addition">+ // used to hold animation value / progress</span>
<span class="hljs-addition">+  const headerScroll = useSharedValue(0);</span>

<span class="hljs-addition">+ // scroll event handler reference</span>
<span class="hljs-addition">+  const scrollHandler = useAnimatedScrollHandler((event) =&gt; {</span>
<span class="hljs-addition">+    headerScroll.value = event.contentOffset.y;</span>
<span class="hljs-addition">+  });</span>

  return (
    &lt;StyledView flex={1} alignItems='center' backgroundColor='background'&gt;
<span class="hljs-deletion">-      &lt;StyledScrollView</span>
<span class="hljs-addition">+      &lt;AnimatedScrollView</span>
        width='100%'
        px={16}
        contentContainerStyle={{
<span class="hljs-deletion">-          paddingTop: HEADER_FULL_HEIGHT + SEARCH_INPUT_HEIGHT + PADDING_VERTICAL_SEARCH,</span>
<span class="hljs-addition">+          paddingTop: HEADER_FULL_HEIGHT + SEARCH_BAR_HEIGHT + PADDING_TOP_BAR * 2,</span>
        }}
<span class="hljs-addition">+        keyboardShouldPersistTaps='never'</span>
<span class="hljs-addition">+        onScroll={scrollHandler}</span>
<span class="hljs-addition">+        scrollEventThrottle={16}</span>
<span class="hljs-addition">+        bounces={false}</span>
      &gt;
        &lt;MasonryList
          notes={filteredNotes}
          search={search}
          isLoading={isLoading}
          userHasNotes={!!notes &amp;&amp; notes?.length &gt; 0}
        /&gt;
<span class="hljs-deletion">-      &lt;/StyledScrollView&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedScrollView&gt;</span>
      {notes &amp;&amp; notes?.length &gt; 0 &amp;&amp; (
        &lt;SearchBar
          value={search}
          onChangeText={setSearch}
          placeholder='Search a title...'
<span class="hljs-addition">+          scroll={headerScroll}</span>
        /&gt;
      )}
      &lt;CreateButton /&gt;
<span class="hljs-deletion">-      &lt;Header /&gt;</span>
<span class="hljs-addition">+      &lt;Header scroll={headerScroll} /&gt;</span>
    &lt;/StyledView&gt;
  );
}
</code></pre>
<h3 id="heading-header">Header</h3>
<p>Now, we need to update our component to be able to use the new prop passed by the parent (named <code>scroll</code>). Then, we will animate our component style according to the scroll value.</p>
<p>(more explanations below)</p>
<pre><code class="lang-diff">// src/components/Header.tsx
const TWITTER_NAME = '@dpnick_';
<span class="hljs-deletion">-export const HEADER_FULL_HEIGHT = 300;</span>

<span class="hljs-addition">+const AnimatedHeaderView = Animated.createAnimatedComponent(StyledView);</span>

<span class="hljs-addition">+interface HeaderProps {</span>
<span class="hljs-addition">+  scroll: Animated.SharedValue&lt;number&gt;;</span>
<span class="hljs-addition">+}</span>

<span class="hljs-deletion">- export default function Header() {</span>
<span class="hljs-addition">+ export default function Header({ scroll }: HeaderProps) {</span>
  const { isDarkTheme, switchTheme } = useContext(ThemeContext);

  const openTwitter = async () =&gt; {...};

<span class="hljs-addition">+ // animated style based on scroll shared value from parent</span>
<span class="hljs-addition">+  const headerAnimatedStyle = useAnimatedStyle(() =&gt; {</span>
<span class="hljs-addition">+    const height = interpolate(</span>
<span class="hljs-addition">+      scroll.value,</span>
<span class="hljs-addition">+      [0, HEADER_SCROLL_DISTANCE],</span>
<span class="hljs-addition">+      [HEADER_FULL_HEIGHT, HEADER_MIN_HEIGHT],</span>
<span class="hljs-addition">+      Extrapolate.CLAMP</span>
<span class="hljs-addition">+    );</span>
<span class="hljs-addition">+    const borderRadius = interpolate(</span>
<span class="hljs-addition">+      scroll.value,</span>
<span class="hljs-addition">+      [0, HEADER_SCROLL_DISTANCE],</span>
<span class="hljs-addition">+      [48, 24],</span>
<span class="hljs-addition">+      Extrapolate.CLAMP</span>
<span class="hljs-addition">+    );</span>
<span class="hljs-addition">+    return {</span>
<span class="hljs-addition">+      height,</span>
<span class="hljs-addition">+      borderBottomRightRadius: borderRadius,</span>
<span class="hljs-addition">+      borderBottomLeftRadius: borderRadius,</span>
<span class="hljs-addition">+    };</span>
<span class="hljs-addition">+  });</span>

  return (
<span class="hljs-deletion">-    &lt;StyledView</span>
<span class="hljs-addition">+    &lt;AnimatedHeaderView</span>
      bg='primary'
      position='absolute'
      width='100%'
      justifyContent='center'
<span class="hljs-addition">+      style={headerAnimatedStyle}</span>
    &gt;
      &lt;StyledView padding={4}&gt;
        &lt;StyledText fontSize={6} color='white'&gt;
          Welcome
        &lt;/StyledText&gt;
        &lt;Pressable
          onPress={openTwitter}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          &lt;StyledText fontSize={6} color='secondary' paddingLeft={36}&gt;
            {TWITTER_NAME}
          &lt;/StyledText&gt;
        &lt;/Pressable&gt;
      &lt;/StyledView&gt;
      &lt;StyledView
        position='absolute'
        top={(StatusBar.currentHeight || 40) + 8}
        right={16}
      &gt;
        &lt;Pressable
          onPress={switchTheme}
          hitSlop={16}
          android_ripple={{ borderless: true, color: 'white' }}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          &lt;Feather
            name={isDarkTheme ? 'sun' : 'moon'}
            size={24}
            color='white'
          /&gt;
        &lt;/Pressable&gt;
      &lt;/StyledView&gt;
<span class="hljs-deletion">-    &lt;/StyledView&gt;</span>
<span class="hljs-addition">+    &lt;/AnimatedHeaderView&gt;</span>
  );
}
</code></pre>
<p>💡 As you may have noticed we used a hook called <code>useAnimatedStyle</code>. It is one of the most important piece of Reanimated. It allows you to create an association between a shared value and your animated component properties.</p>
<p>In this hook we are animating both the height and border radius of our header according to the actual scroll value from the parent.</p>
<blockquote>
<p>"But wait... how to find a corresponding value between the header height and the scroll value ?"</p>
</blockquote>
<p>Let's decrypt an example from our code:</p>
<pre><code class="lang-jsx">    <span class="hljs-keyword">const</span> height = interpolate(
      scroll.value, <span class="hljs-comment">// shared value to apply the interpolate</span>
      [<span class="hljs-number">0</span>, HEADER_SCROLL_DISTANCE], <span class="hljs-comment">// input range</span>
      [HEADER_FULL_HEIGHT, HEADER_MIN_HEIGHT], <span class="hljs-comment">// output range</span>
      Extrapolate.CLAMP <span class="hljs-comment">// extrapolation type</span>
    );
</code></pre>
<p>We used an <code>interpolate</code> function. It allows us to convert an input range (in our case the scroll value) to an output one (corresponding header size). We defined bounds (min and max) for each range, each value between will be proportional. Thanks to the <code>clamp</code> extrapolation every value outside of our bounds will be ignored.</p>
<p>So in plain words it would give the following :</p>
<ul>
<li>when the scroll value is 0 we want the header to display full height.</li>
<li>as soon as you start scrolling the header height must change accordingly.</li>
<li>once you hit the total scroll distance, it means the height of your header is at its min height and won't reduce more.</li>
</ul>
<p>(same principle in the other way)</p>
<p>From now on, you are officially able to create scrolling-aware animations 🚀</p>
<h3 id="heading-search-bar">Search bar</h3>
<p>It will be very similar to what we did just before for the header. If you feel confident enough, try it by yourself, the best way to learn is by getting your hands dirty. Don't worry I will still include my solution below 😄</p>
<p>The animation we want to build for this component is about <strong>translateY</strong> property : when the user scroll, the search bar must move with our header. At the end of the scroll the search bar must be inside the header as you can see here :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1641760724753/vSHKQCtEy.png" alt="Capture d’écran 2022-01-07 à 16.14.25.png" /></p>
<p>My solution (of course, others are possible) :</p>
<pre><code class="lang-diff">// src/components/SearchBar.tsx
interface SearchBarInterface {
  value: string;
  onChangeText: (next: string) =&gt; void;
  placeholder: string;
<span class="hljs-addition">+  scroll: Animated.SharedValue&lt;number&gt;;</span>
}

<span class="hljs-addition">+const AnimatedSearchView = Animated.createAnimatedComponent(StyledView);</span>

export default function SearchBar({
  value,
  onChangeText,
  placeholder,
<span class="hljs-addition">+  scroll,</span>
}: SearchBarInterface) {

<span class="hljs-addition">+  const searchAnimatedStyle = useAnimatedStyle(() =&gt; {</span>
<span class="hljs-addition">+    const translateY = interpolate(</span>
<span class="hljs-addition">+      scroll.value,</span>
<span class="hljs-addition">+      [0, HEADER_SCROLL_DISTANCE],</span>
<span class="hljs-addition">+      [</span>
<span class="hljs-addition">+        HEADER_FULL_HEIGHT + PADDING_TOP_BAR,</span>
<span class="hljs-addition">+        HEADER_MIN_HEIGHT - SEARCH_BAR_HEIGHT - PADDING_TOP_BAR,</span>
<span class="hljs-addition">+      ],</span>
<span class="hljs-addition">+      Extrapolate.CLAMP</span>
<span class="hljs-addition">+    );</span>
<span class="hljs-addition">+    return {</span>
<span class="hljs-addition">+      transform: [{ translateY }],</span>
<span class="hljs-addition">+    };</span>
<span class="hljs-addition">+  });</span>

  return (
<span class="hljs-deletion">-    &lt;StyledView</span>
<span class="hljs-addition">+    &lt;AnimatedSearchView</span>
      px={20}
      pb={16}
<span class="hljs-addition">+      zIndex={1}</span>
      width='100%'
      position='absolute'
<span class="hljs-addition">+      style={searchAnimatedStyle}</span>
<span class="hljs-addition">+      backgroundColor='transparent'</span>
    &gt;
      &lt;StyledView
        bg='accent'
        borderRadius={8}
        flexDirection='row'
        justifyContent='flex-start'
        alignItems='center'
        height={40}
      &gt;
        &lt;Feather
          name='search'
          size={20}
          color='black'
          style={styles.iconStart}
        /&gt;
        &lt;TextInput
          style={styles.input}
          placeholder={placeholder}
          value={value}
          onChangeText={onChangeText}
        /&gt;
        {value?.length &gt; 0 &amp;&amp; (
          &lt;Feather
            name='x'
            size={24}
            color='black'
            style={styles.iconEnd}
            onPress={() =&gt; onChangeText('')}
          /&gt;
        )}
      &lt;/StyledView&gt;
<span class="hljs-deletion">-    &lt;/StyledView&gt;</span>
<span class="hljs-addition">+    &lt;/AnimatedSearchView&gt;</span>
  );
}
</code></pre>
<p>After that you should have animation very close to what we are aiming for !
But as you can see, our welcome message doesn't fit our header in "shrink" state. Let's take care of it!</p>
<h3 id="heading-header-content">Header content</h3>
<p>The objective of this animation is the replace the header content when in "shrink" state.</p>
<p>After experimenting a little bit around it, the best user experience (to me at least) seems to be as follow : </p>
<ul>
<li>display the "full" title until reaching half our scrolling distance</li>
<li>at this same distance, start displaying progressively our "shrink" title</li>
</ul>
<pre><code class="lang-diff">// src/components/Header.tsx
// omitted previous code for conciseness

const AnimatedHeaderView = Animated.createAnimatedComponent(StyledView);
<span class="hljs-addition">+const AnimatedText = Animated.createAnimatedComponent(StyledText);</span>

interface HeaderProps {
  scroll: Animated.SharedValue&lt;number&gt;;
}

export default function Header({ scroll }: HeaderProps) {
  // omitted previous code for conciseness

<span class="hljs-addition">+  const titleOpacity = useAnimatedStyle(() =&gt; {</span>
<span class="hljs-addition">+    const opacity = interpolate(</span>
<span class="hljs-addition">+      scroll.value,</span>
<span class="hljs-addition">+      [0, HEADER_SCROLL_DISTANCE / 2],</span>
<span class="hljs-addition">+      [1, 0],</span>
<span class="hljs-addition">+      Extrapolate.CLAMP</span>
<span class="hljs-addition">+    );</span>

<span class="hljs-addition">+    return {</span>
<span class="hljs-addition">+      opacity,</span>
<span class="hljs-addition">+    };</span>
<span class="hljs-addition">+  });</span>

<span class="hljs-addition">+  const reverseTitleOpacity = useAnimatedStyle(() =&gt; {</span>
<span class="hljs-addition">+    const opacity = interpolate(</span>
<span class="hljs-addition">+      scroll.value,</span>
<span class="hljs-addition">+      [HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE],</span>
<span class="hljs-addition">+      [0, 1],</span>
<span class="hljs-addition">+      Extrapolate.CLAMP</span>
<span class="hljs-addition">+    );</span>

<span class="hljs-addition">+    return {</span>
<span class="hljs-addition">+      opacity,</span>
<span class="hljs-addition">+    };</span>
<span class="hljs-addition">+  });</span>

  return (
    &lt;AnimatedHeaderView
      bg='primary'
      position='absolute'
      width='100%'
      justifyContent='center'
      style={headerAnimatedStyle}
    &gt;
<span class="hljs-deletion">-      &lt;StyledView padding={4}&gt;</span>
<span class="hljs-addition">+      &lt;AnimatedHeaderView padding={4} style={titleOpacity}&gt;</span>
<span class="hljs-deletion">-        &lt;StyledText</span>
<span class="hljs-addition">+        &lt;AnimatedText</span>
<span class="hljs-addition">+          entering={FadeInLeft.duration(1000)}</span>
          fontSize={6}
          color='white'
        &gt;
          Welcome
<span class="hljs-deletion">-      &lt;/StyledText&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedText&gt;</span>
        &lt;Pressable
          onPress={openTwitter}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
<span class="hljs-deletion">-        &lt;StyledText</span>
<span class="hljs-addition">+        &lt;AnimatedText</span>
<span class="hljs-addition">+            entering={FadeInLeft.duration(1000).delay(1000)}</span>
            fontSize={6}
            color='secondary'
            paddingLeft={36}
          &gt;
            {TWITTER_NAME}
<span class="hljs-deletion">-      &lt;/StyledText&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedText&gt;</span>
        &lt;/Pressable&gt;
<span class="hljs-deletion">-      &lt;/StyledView&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedHeaderView&gt;</span>
<span class="hljs-addition">+      &lt;AnimatedHeaderView</span>
<span class="hljs-addition">+        position='absolute'</span>
<span class="hljs-addition">+        top={(StatusBar.currentHeight || 40) + 8}</span>
<span class="hljs-addition">+        alignSelf='center'</span>
<span class="hljs-addition">+        style={reverseTitleOpacity}</span>
<span class="hljs-addition">+      &gt;</span>
<span class="hljs-addition">+        &lt;StyledText</span>
<span class="hljs-addition">+          color='white'</span>
<span class="hljs-addition">+          fontSize={3}</span>
<span class="hljs-addition">+        &gt;</span>
<span class="hljs-addition">+          {TWITTER_NAME}</span>
<span class="hljs-addition">+        &lt;/StyledText&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedHeaderView&gt;</span>
      &lt;StyledView
        position='absolute'
        top={(StatusBar.currentHeight || 40) + 8}
        right={16}
      &gt;
        &lt;Pressable
          onPress={switchTheme}
          hitSlop={16}
          android_ripple={{ borderless: true, color: 'white' }}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          &lt;Feather
            name={isDarkTheme ? 'sun' : 'moon'}
            size={24}
            color='white'
          /&gt;
        &lt;/Pressable&gt;
      &lt;/StyledView&gt;
    &lt;/AnimatedHeaderView&gt;
  );
}
</code></pre>
<h2 id="heading-theme-animation">Theme animation</h2>
<p>Our last animation will operate when the user changes the theme of the app. When clicked, the theme icon (either "moon" or "sun") should move outside of the screen, update the theme and then return to its initial position.</p>
<p>This animation will be a good usecase to show you how to use Reanimated helpers :</p>
<ul>
<li><code>withSequence</code> allow to run multiple animations sequentially</li>
<li><code>withTiming</code> create a time based animation</li>
</ul>
<p>(documentation and list of other helpers can be found <a target="_blank" href="https://docs.swmansion.com/react-native-reanimated/docs/next/api/animations/withTiming/">here</a>)</p>
<pre><code class="lang-diff">// src/components/Header

// omitted not-concerned code for conciseness

<span class="hljs-addition">+const ICON_Y_OFFSET = 44;</span>

export default function Header({ scroll }: HeaderProps) {
  const { isDarkTheme, switchTheme } = useContext(ThemeContext);
<span class="hljs-addition">+  const themeAnim = useSharedValue(0);</span>

<span class="hljs-addition">+  const toggleThemeMode = useCallback(() =&gt; {</span>
<span class="hljs-addition">+    themeAnim.value = withSequence(</span>
<span class="hljs-addition">+      // 1st arg =&gt; value where to move</span>
<span class="hljs-addition">+      // 2nd arg =&gt; options (duration and easing)</span>
<span class="hljs-addition">+      // 3rd arg =&gt; finished callback</span>
<span class="hljs-addition">+      // first timing =&gt; move outside then update theme</span>
<span class="hljs-addition">+      withTiming(ICON_Y_OFFSET, undefined, (isFinished) =&gt; {</span>
<span class="hljs-addition">+        if (isFinished) {</span>
<span class="hljs-addition">+          // ensure the function to be called on the JS thread </span>
<span class="hljs-addition">+          runOnJS(switchTheme)();</span>
<span class="hljs-addition">+        }</span>
<span class="hljs-addition">+      }),</span>
<span class="hljs-addition">+      // second timing =&gt; return to initial position</span>
<span class="hljs-addition">+      withTiming(0)</span>
<span class="hljs-addition">+    );</span>
<span class="hljs-addition">+  }, [switchTheme]);</span>

<span class="hljs-addition">+  const themeStyle = useAnimatedStyle(() =&gt; {</span>
<span class="hljs-addition">+    return {</span>
<span class="hljs-addition">+      transform: [{ translateX: themeAnim.value }],</span>
<span class="hljs-addition">+    };</span>
<span class="hljs-addition">+  });</span>

// ...
  return (
    &lt;AnimatedHeaderView
      bg='primary'
      position='absolute'
      width='100%'
      justifyContent='center'
      style={headerAnimatedStyle}
    &gt;
      &lt;AnimatedHeaderView padding={4} style={titleOpacity}&gt;
        &lt;AnimatedText
          entering={FadeInLeft.duration(1000)}
          fontSize={6}
          color='white'
        &gt;
          Welcome
        &lt;/AnimatedText&gt;
        &lt;Pressable
          onPress={openTwitter}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          &lt;AnimatedText
            entering={FadeInLeft.duration(1000).delay(1000)}
            fontSize={6}
            color='secondary'
            paddingLeft={36}
          &gt;
            {TWITTER_NAME}
          &lt;/AnimatedText&gt;
        &lt;/Pressable&gt;
      &lt;/AnimatedHeaderView&gt;
      &lt;AnimatedHeaderView
        position='absolute'
        top={(StatusBar.currentHeight || 40) + 8}
        alignSelf='center'
        style={reverseTitleOpacity}
      &gt;
        &lt;StyledText color='white' fontSize={3}&gt;
          {TWITTER_NAME}
        &lt;/StyledText&gt;
      &lt;/AnimatedHeaderView&gt;
<span class="hljs-deletion">-      &lt;StyledView</span>
<span class="hljs-addition">+      &lt;AnimatedHeaderView</span>
<span class="hljs-addition">+        style={themeStyle}</span>
        position='absolute'
        top={(StatusBar.currentHeight || 40) + 8}
        right={16}
      &gt;
        &lt;Pressable
<span class="hljs-deletion">-          onPress={switchTheme}</span>
<span class="hljs-addition">+          onPress={toggleThemeMode}</span>
          hitSlop={16}
          android_ripple={{ borderless: true, color: 'white' }}
          style={({ pressed }) =&gt; ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          &lt;Feather
            name={isDarkTheme ? 'sun' : 'moon'}
            size={24}
            color='white'
          /&gt;
        &lt;/Pressable&gt;
<span class="hljs-deletion">-      &lt;/StyledView&gt;</span>
<span class="hljs-addition">+      &lt;/AnimatedHeaderView&gt;</span>
    &lt;/AnimatedHeaderView&gt;
  );
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations, you have completed this entire series! You now have a fully functional and smoothly animated note-taking app! But this is only the beginning, you can keep improving it by adding new features. For example, it could be really interesting for the users to store their notes on a remote server (Firebase, Supabase or custom back-end) to be able to access it on all their devices.</p>
<p>Thanks for reading, I hope this series has been helpful to you, do not hesitate to leave a comment or to join me on <a target="_blank" href="https://twitter.com/dpnick_">twitter</a> I would be really happy to get your feedbacks and your ideas for the next topic.</p>
<p>See you next time!</p>
<p>- Yannick 👋</p>
]]></content:encoded></item><item><title><![CDATA[How to easily manage a global state with the context API]]></title><description><![CDATA[Welcome back 👋
Happy to see you again! In this second article of the series, we are going to take advantage of our configuration to efficiently design our pages/components and implement our business logic. 

If you didn't read the first part I recom...]]></description><link>https://code-with-yannick.com/how-to-manage-global-state-with-context</link><guid isPermaLink="true">https://code-with-yannick.com/how-to-manage-global-state-with-context</guid><category><![CDATA[React Native]]></category><category><![CDATA[Expo]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Wed, 08 Dec 2021 14:10:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638916349491/LS7yJWiyg.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-welcome-back">Welcome back 👋</h1>
<p>Happy to see you again! In this second article of the series, we are going to take advantage of our configuration to efficiently design our pages/components and implement our business logic. </p>
<blockquote>
<p>If you didn't read the first part I recommend you to check it out first by clicking <a target="_blank" href="https://code-with-yannick.com/how-to-set-up-your-react-native-project">here</a></p>
</blockquote>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/lxHXdlKInWY">https://youtu.be/lxHXdlKInWY</a></div>
<p>We will start by creating our business data management. In our case, it is the user notes. Once done, it will be easier to build the corresponding UI components.</p>
<p>Let's dive right in!</p>
<h2 id="heading-note-context">Note context</h2>
<p>First of all, let's define what will be a <code>Note</code> object</p>
<p>I suggest to create the following data model <code>src/models/note.ts</code>:</p>
<pre><code class="lang-ts"><span class="hljs-comment">// src/models/note.ts</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Note {
  <span class="hljs-comment">// unique identifier</span>
  id: <span class="hljs-built_in">number</span>;
  title: <span class="hljs-built_in">string</span>;
  createdAt: <span class="hljs-built_in">number</span>;
  updatedAt: <span class="hljs-built_in">number</span>;
  content: <span class="hljs-built_in">string</span>;
  <span class="hljs-comment">// Card background color</span>
  color: NoteColors;
}

<span class="hljs-keyword">export</span> <span class="hljs-built_in">enum</span> NoteColors {
  PRIMARY = <span class="hljs-string">'primary'</span>,
  SECONDARY = <span class="hljs-string">'secondary'</span>,
}
</code></pre>
<blockquote>
<p>Both <code>createdAt</code> and <code>updatedAt</code> are timestamps. Timestamps are easier to manage/manipulate compare to Date objects. The fact remains that we want it to be understandable by the user, so we will use one of my favorite library <code>date-fns</code> (it is just like <code>moment</code> but... light 😅) </p>
</blockquote>
<pre><code class="lang-bash">npm i date-fns &amp;&amp; npm i --save-dev @types/date-fns
</code></pre>
<p>Go ahead and add the following new value to our <code>StorageKeys</code> enum at <code>src/models/storage.ts</code> :</p>
<pre><code class="lang-diff">export enum StorageKeys {
  USE_DARK_THEME = 'useDarkTheme',
<span class="hljs-addition">+  USER_NOTES = 'userNotes',</span>
}
</code></pre>
<p>Once it's done, let's add a new file <code>src/contexts/NoteContexts.tsx</code></p>
<p>In this file, we are going to implement our business logic, in our case, it means: how are we handling user notes. There are a lot of possibilities to manage data (state component, context API, redux, recoil...), you must choose wisely depending on your needs (complexity, scalability, performance...). In our case, the application is quite small and each page will have a dependency on the notes data. Therefore, we'll keep it simple and use the built-in react context API.</p>
<blockquote>
<p>The built-in React context API is a really powerful way to manage data through the component tree. However, you should use it carefully, contexts are only intended to be used to share <code>global</code> data. Over-using contexts can result in performance issues.</p>
</blockquote>
<p>Once you choose how to manage your data, you need to define your features. In most cases (like us), it will be a CRUD (Create-Read-Update-Delete). Our users will be able to :</p>
<ul>
<li>create a new note</li>
<li>delete an existing note</li>
<li>update an existing note</li>
<li>select an existing note to read its content</li>
</ul>
<p>To easily manage those actions we will use the <code>useReducer</code> hook in our context. If you already used it, it works like redux: you define a state then dispatch actions to a reducer. This reducer, is basically a switch function, for each action it knows how to update the state and then return its next value. This state will then be spread to our components thanks to the context.</p>
<p>This is the "difficult" part of this tutorial, if you need further explanations do not hesitate to leave a comment I will be happy to help you 🙂</p>
<p>First, update the <code>src/models/note.ts</code> to add the following : </p>
<pre><code class="lang-ts"><span class="hljs-comment">// ... Note interface</span>

<span class="hljs-comment">// interface of our context</span>
<span class="hljs-comment">// it expose both our values and functions to update it</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> INoteContext {
  notes: Note[] | <span class="hljs-literal">undefined</span>;
  selectedNote: Note | <span class="hljs-literal">undefined</span>;
  selectNote: <span class="hljs-function">(<span class="hljs-params">id?: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  deleteNote: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  createNote: <span class="hljs-function">(<span class="hljs-params">title: <span class="hljs-built_in">string</span>, content: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  updateNote: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span>, title: <span class="hljs-built_in">string</span>, content: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">void</span>;
  updateNoteColor: <span class="hljs-function">(<span class="hljs-params">id: <span class="hljs-built_in">number</span>, color: NoteColors</span>) =&gt;</span> <span class="hljs-built_in">void</span>;
}

<span class="hljs-comment">// All our possible actions</span>
<span class="hljs-comment">// using enum prevent typo</span>
<span class="hljs-keyword">export</span> <span class="hljs-built_in">enum</span> NoteActionTypes {
  LOAD_NOTES = <span class="hljs-string">'load-notes'</span>,
  SELECT_NOTE = <span class="hljs-string">'select-note'</span>,
  DELETE_NOTE = <span class="hljs-string">'delete-note'</span>,
  CREATE_NOTE = <span class="hljs-string">'create-note'</span>,
  UPDATE_NOTE = <span class="hljs-string">'update-note'</span>,
  UPDATE_NOTE_COLOR = <span class="hljs-string">'update-note-color'</span>,
}

<span class="hljs-comment">// each action is composed of a type and payload</span>
<span class="hljs-comment">// each payload can be different</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> NoteAction =
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.LOAD_NOTES; payload: { notes: Note[] } }
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.SELECT_NOTE; payload: { id: <span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span> } }
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.DELETE_NOTE; payload: { notes: Note[] } }
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.CREATE_NOTE; payload: { note: Note } }
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.UPDATE_NOTE; payload: { notes: Note[] } }
  | { <span class="hljs-keyword">type</span>: NoteActionTypes.UPDATE_NOTE_COLOR; payload: { notes: Note[] } };

<span class="hljs-comment">// data in our state</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> NoteState {
  notes: Note[] | <span class="hljs-literal">undefined</span>;
  selectedId: <span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span>;
}
</code></pre>
<p>And then, in our <code>src/contexts/NoteContext.tsx</code></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/contexts/NoteContext.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-keyword">const</span> NoteContext = createContext&lt;INoteContext&gt;({
  <span class="hljs-attr">notes</span>: <span class="hljs-literal">undefined</span>,
  <span class="hljs-attr">selectedNote</span>: <span class="hljs-literal">undefined</span>,
  <span class="hljs-attr">selectNote</span>: <span class="hljs-function">() =&gt;</span> {},
  <span class="hljs-attr">deleteNote</span>: <span class="hljs-function">() =&gt;</span> {},
  <span class="hljs-attr">createNote</span>: <span class="hljs-function">() =&gt;</span> {},
  <span class="hljs-attr">updateNote</span>: <span class="hljs-function">() =&gt;</span> {},
  <span class="hljs-attr">updateNoteColor</span>: <span class="hljs-function">() =&gt;</span> {},
});

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useNote</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> useContext(NoteContext);
}

<span class="hljs-keyword">const</span> loadNotes = (notes: Note[]): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.LOAD_NOTES,
    <span class="hljs-attr">payload</span>: {
      notes,
    },
  };
};

<span class="hljs-keyword">const</span> selectNote = (id?: number): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.SELECT_NOTE,
    <span class="hljs-attr">payload</span>: {
      id,
    },
  };
};

<span class="hljs-keyword">const</span> deleteNote = (notes: Note[]): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.DELETE_NOTE,
    <span class="hljs-attr">payload</span>: {
      notes,
    },
  };
};

<span class="hljs-keyword">const</span> createNote = (note: Note): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.CREATE_NOTE,
    <span class="hljs-attr">payload</span>: {
      note,
    },
  };
};

<span class="hljs-keyword">const</span> updateNote = (notes: Note[]): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.UPDATE_NOTE,
    <span class="hljs-attr">payload</span>: {
      notes,
    },
  };
};

<span class="hljs-keyword">const</span> updateNoteColor = (notes: Note[]): <span class="hljs-function"><span class="hljs-params">NoteAction</span> =&gt;</span> {
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">type</span>: NoteActionTypes.UPDATE_NOTE_COLOR,
    <span class="hljs-attr">payload</span>: {
      notes,
    },
  };
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reducer</span>(<span class="hljs-params">state: NoteState, action: NoteAction</span>): <span class="hljs-title">NoteState</span> </span>{
  <span class="hljs-keyword">switch</span> (action.type) {
    <span class="hljs-keyword">case</span> NoteActionTypes.LOAD_NOTES:
      <span class="hljs-keyword">return</span> {
        ...state,
        <span class="hljs-attr">notes</span>: action.payload.notes,
      };
    <span class="hljs-keyword">case</span> NoteActionTypes.SELECT_NOTE:
      <span class="hljs-keyword">return</span> {
        ...state,
        <span class="hljs-attr">selectedId</span>: action.payload.id,
      };
    <span class="hljs-keyword">case</span> NoteActionTypes.DELETE_NOTE:
      <span class="hljs-keyword">return</span> {
        ...state,
        <span class="hljs-attr">notes</span>: action.payload.notes,
      };
    <span class="hljs-keyword">case</span> NoteActionTypes.CREATE_NOTE:
      <span class="hljs-keyword">if</span> (state.notes) {
        <span class="hljs-keyword">return</span> {
          ...state,
          <span class="hljs-attr">notes</span>: [...state.notes, action.payload.note],
          <span class="hljs-attr">selectedId</span>: action.payload.note.id,
        };
      }
      <span class="hljs-keyword">return</span> state;
    <span class="hljs-keyword">case</span> NoteActionTypes.UPDATE_NOTE:
      <span class="hljs-keyword">return</span> {
        ...state,
        <span class="hljs-attr">notes</span>: action.payload.notes,
      };
    <span class="hljs-keyword">case</span> NoteActionTypes.UPDATE_NOTE_COLOR:
      <span class="hljs-keyword">return</span> {
        ...state,
        <span class="hljs-attr">notes</span>: action.payload.notes,
      };
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">return</span> state;
  }
}

<span class="hljs-keyword">const</span> NoteProvider = <span class="hljs-function">(<span class="hljs-params">{ children }: { children: React.ReactNode }</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(reducer, {
    <span class="hljs-attr">notes</span>: <span class="hljs-literal">undefined</span>,
    <span class="hljs-attr">selectedId</span>: <span class="hljs-literal">undefined</span>,
  });

  <span class="hljs-comment">// get user notes on init</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> getNotes = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-comment">// instead of calling async storage in a real case</span>
      <span class="hljs-comment">// you would here make a fetch request to your API</span>
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">let</span> notes = [];
        <span class="hljs-keyword">const</span> savedNotes = <span class="hljs-keyword">await</span> AsyncStorage.getItem(StorageKeys.USER_NOTES);
        <span class="hljs-keyword">if</span> (savedNotes !== <span class="hljs-literal">null</span>) {
          notes = <span class="hljs-built_in">JSON</span>.parse(savedNotes);
        }
        dispatch(loadNotes(notes));
      } <span class="hljs-keyword">catch</span> {
        Alert.alert(<span class="hljs-string">'An error occurred while getting your notes'</span>);
      }
    };
    getNotes();
  }, []);

  <span class="hljs-keyword">const</span> onSelectNote = <span class="hljs-function">(<span class="hljs-params">id?: number</span>) =&gt;</span> {
    dispatch(selectNote(id));
  };

  <span class="hljs-keyword">const</span> onDeleteNote = <span class="hljs-keyword">async</span> (id: number) =&gt; {
    <span class="hljs-comment">// guard to prevent running if notes is not defined</span>
    <span class="hljs-keyword">if</span> (!state.notes) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> nextNotes = state.notes.filter(<span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> note.id !== id);
    <span class="hljs-comment">// call to your api (async storage for demo purpose)</span>
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> AsyncStorage.setItem(
        StorageKeys.USER_NOTES,
        <span class="hljs-built_in">JSON</span>.stringify(nextNotes)
      );
      dispatch(deleteNote(nextNotes));
    } <span class="hljs-keyword">catch</span> {
      Alert.alert(<span class="hljs-string">'An error occurred while deleting your note'</span>);
    }
  };

  <span class="hljs-keyword">const</span> onCreateNote = <span class="hljs-keyword">async</span> (title: string, <span class="hljs-attr">content</span>: string) =&gt; {
    <span class="hljs-comment">// guard to prevent running if notes is not defined</span>
    <span class="hljs-keyword">if</span> (!state.notes) <span class="hljs-keyword">return</span>;
    <span class="hljs-comment">// in real case you could use uuid generator</span>
    <span class="hljs-keyword">const</span> max = state.notes?.reduce(
      <span class="hljs-function">(<span class="hljs-params">prev, current</span>) =&gt;</span> (prev &gt; current.id ? prev : current.id),
      <span class="hljs-number">0</span>
    );
    <span class="hljs-keyword">const</span> newNote: Note = {
      <span class="hljs-attr">id</span>: max + <span class="hljs-number">1</span>,
      <span class="hljs-attr">createdAt</span>: <span class="hljs-built_in">Date</span>.now(),
      <span class="hljs-attr">updatedAt</span>: <span class="hljs-built_in">Date</span>.now(),
      title,
      content,
      <span class="hljs-attr">color</span>: NoteColors.PRIMARY,
    };
    <span class="hljs-comment">// call to your api (async storage for demo purpose)</span>
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> AsyncStorage.setItem(
        StorageKeys.USER_NOTES,
        <span class="hljs-built_in">JSON</span>.stringify([...state.notes, newNote])
      );
      dispatch(createNote(newNote));
    } <span class="hljs-keyword">catch</span> {
      Alert.alert(<span class="hljs-string">'An error occurred while deleting your note'</span>);
    }
  };

  <span class="hljs-keyword">const</span> onUpdateNote = <span class="hljs-keyword">async</span> (id: number, <span class="hljs-attr">title</span>: string, <span class="hljs-attr">content</span>: string) =&gt; {
    <span class="hljs-comment">// guard to prevent running if notes is not defined</span>
    <span class="hljs-keyword">if</span> (!state.notes) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> updatedNote: Note | <span class="hljs-literal">undefined</span> = state.notes.find(
      <span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> note.id === id
    );
    <span class="hljs-keyword">if</span> (updatedNote) {
      updatedNote.title = title;
      updatedNote.content = content;
      updatedNote.updatedAt = <span class="hljs-built_in">Date</span>.now();
      <span class="hljs-keyword">const</span> nextNotes = [
        ...state.notes.filter(<span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> note.id !== id),
        updatedNote,
      ];
     <span class="hljs-comment">// call to your api (async storage for demo purpose)</span>
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> AsyncStorage.setItem(
          StorageKeys.USER_NOTES,
          <span class="hljs-built_in">JSON</span>.stringify(nextNotes)
        );
        dispatch(updateNote(nextNotes));
      } <span class="hljs-keyword">catch</span> {
        Alert.alert(<span class="hljs-string">'An error occurred while deleting your note'</span>);
      }
    }
  };

  <span class="hljs-keyword">const</span> onUpdateNoteColor = <span class="hljs-keyword">async</span> (id: number, <span class="hljs-attr">color</span>: NoteColors) =&gt; {
    <span class="hljs-comment">// guard to prevent running if notes is not defined</span>

    <span class="hljs-keyword">if</span> (!state.notes) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">const</span> updatedNote: Note | <span class="hljs-literal">undefined</span> = state.notes.find(
      <span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> note.id === id
    );
    <span class="hljs-keyword">if</span> (updatedNote) {
      updatedNote.color = color;
      updatedNote.updatedAt = <span class="hljs-built_in">Date</span>.now();
      <span class="hljs-keyword">const</span> nextNotes = [
        ...state.notes.filter(<span class="hljs-function">(<span class="hljs-params">note</span>) =&gt;</span> note.id !== id),
        updatedNote,
      ];
      <span class="hljs-comment">// call to your api (async storage for demo purpose)</span>
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> AsyncStorage.setItem(
          StorageKeys.USER_NOTES,
          <span class="hljs-built_in">JSON</span>.stringify(nextNotes)
        );
        dispatch(updateNoteColor(nextNotes));
      } <span class="hljs-keyword">catch</span> {
        Alert.alert(<span class="hljs-string">'An error occurred while deleting your note'</span>);
      }
    }
  };

  <span class="hljs-comment">// our context forward both our state and the functions to update it to its children</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NoteContext.Provider</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">notes:</span> <span class="hljs-attr">state.notes</span>,
        <span class="hljs-attr">selectedNote:</span> <span class="hljs-attr">state.notes</span>?<span class="hljs-attr">.find</span>((<span class="hljs-attr">note</span>) =&gt;</span> note.id === state.selectedId),
        selectNote: onSelectNote,
        deleteNote: onDeleteNote,
        createNote: onCreateNote,
        updateNote: onUpdateNote,
        updateNoteColor: onUpdateNoteColor,
      }}
    &gt;
      {children}
    <span class="hljs-tag">&lt;/<span class="hljs-name">NoteContext.Provider</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> NoteProvider;
</code></pre>
<p>Once its done, use it in your <code>App.tsx</code> just as we did for the <code>ThemeProvider</code></p>
<pre><code class="lang-diff">// App.tsx
// ...imports
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
<span class="hljs-addition">+    &lt;SafeAreaProvider&gt;</span>
      &lt;ThemeProvider&gt;
<span class="hljs-addition">+        &lt;NoteProvider&gt;</span>
          &lt;Navigation /&gt;
          &lt;StatusBar style='auto' animated /&gt;
<span class="hljs-addition">+        &lt;/NoteProvider&gt;</span>
      &lt;/ThemeProvider&gt;
<span class="hljs-addition">+    &lt;/SafeAreaProvider&gt;</span>
  );
}
</code></pre>
<p>From now on, every components can have access to our notes state and functions through the <code>useNote</code> hook !</p>
<p>Take the time your need to understand this context, this is really important as developer to master the concept of application state.</p>
<p>Let's make use of it in our note edit page!</p>
<h2 id="heading-note-edit">Note Edit</h2>
<p>In this page, users will be able to update note's content, title and color. Changes will be saved automatically using a debounce mechanism. User can also delete a note, either by clicking a button or by removing all its content / title and going back to the home page.</p>
<p>Start by creating a hook to handle debounce on state change :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/hooks/useDebounce.ts</span>
<span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-comment">// pass a value and a delay</span>
<span class="hljs-comment">// the debouncedValue change to the value passed when the delay is over</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useDebounce</span>&lt;<span class="hljs-title">T</span>&gt;(<span class="hljs-params">value: T, delay: number</span>): <span class="hljs-title">T</span> </span>{
  <span class="hljs-keyword">const</span> [debouncedValue, setDebouncedValue] = useState&lt;T&gt;(value);
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> handler = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      setDebouncedValue(value);
    }, delay);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">clearTimeout</span>(handler);
    };
  }, [value, delay]);
  <span class="hljs-keyword">return</span> debouncedValue;
}
</code></pre>
<p>We must also take care of handling large content, it means we need to implement a scroll to allow the user to see the entire note. Scrolling while editing (keyboard open) can be tricky to manage (content hidden by keyboard etc), I will use a library called <code>react-native-keyboard-aware-scroll-view</code> to solve this.</p>
<pre><code class="lang-bash">npm i react-native-keyboard-aware-scroll-view
</code></pre>
<p>Finally, I created a small funny background component, it simply displays a low-opacity circle at a random position. The circle use the selected color for the note.</p>
<p>Let's add it <code>src/components/NoteEditBackground.tsx</code> </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/NoteEditBackground.tsx</span>
<span class="hljs-comment">// ...imports</span>
<span class="hljs-keyword">const</span> screen = Dimensions.get(<span class="hljs-string">'screen'</span>);

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getRandomInRange</span>(<span class="hljs-params">min: number, max: number</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * (max - min) + min);
}

interface NoteEditBackgroundProps {
  <span class="hljs-attr">color</span>: string;
}

<span class="hljs-comment">// this component is just a funny circle background with random positioning</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NoteEditBackground</span>(<span class="hljs-params">{ color }: NoteEditBackgroundProps</span>) </span>{
  <span class="hljs-keyword">const</span> [leftOffset, setLeftOffset] = useState&lt;number&gt;();
  <span class="hljs-keyword">const</span> [topOffset, setTopOffset] = useState&lt;number&gt;();
  <span class="hljs-keyword">const</span> { width, height } = screen;
  <span class="hljs-keyword">const</span> shapeSize = width;

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// you can experiment with those values to find a result that suits you</span>
    setLeftOffset(getRandomInRange(-shapeSize / <span class="hljs-number">2</span>, shapeSize / <span class="hljs-number">2</span>));
    setTopOffset(getRandomInRange(<span class="hljs-number">0</span> - shapeSize / <span class="hljs-number">2</span>, height - shapeSize / <span class="hljs-number">2</span>));
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
      <span class="hljs-attr">position</span>=<span class="hljs-string">'absolute'</span>
      <span class="hljs-attr">width</span>=<span class="hljs-string">{shapeSize}</span>
      <span class="hljs-attr">height</span>=<span class="hljs-string">{shapeSize}</span>
      <span class="hljs-attr">left</span>=<span class="hljs-string">{leftOffset}</span>
      <span class="hljs-attr">top</span>=<span class="hljs-string">{topOffset}</span>
      <span class="hljs-attr">pointerEvents</span>=<span class="hljs-string">'none'</span>
      <span class="hljs-attr">borderRadius</span>=<span class="hljs-string">{shapeSize</span> / <span class="hljs-attr">2</span>}
      <span class="hljs-attr">opacity</span>=<span class="hljs-string">{0.2}</span>
      <span class="hljs-attr">bg</span>=<span class="hljs-string">{color}</span>
      <span class="hljs-attr">zIndex</span>=<span class="hljs-string">{-1}</span>
    /&gt;</span></span>
  );
}
</code></pre>
<p>And then update our <code>NoteEdit.tsx</code> page to reflect what we stated above.</p>
<blockquote>
<p>You may have noticed, in the following code we use the <code>StyledPressable</code> component. It doesn't exist from the precedent article. This is your opportunity to prove you understood <em>styled-components</em> and <em>styled-system</em> 😁 (If you want to check mine, it is available in my <a target="_blank" href="https://github.com/dpnick/react-native-note-tutorial">github repository</a> 😉)</p>
</blockquote>
<p>I annotated my code to help you understand each purpose.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/pages/NoteEdit.tsx</span>
<span class="hljs-comment">// ...imports</span>

type StyledInputProps = ColorProps &amp;
  SpaceProps &amp;
  FlexboxProps &amp;
  LayoutProps &amp;
  TypographyProps;

<span class="hljs-keyword">const</span> StyledInput = styled.TextInput&lt;TextInputProps &amp; StyledInputProps&gt;(
  compose(color, space, flexbox, layout, typography)
);

type NoteEditProps = NativeStackScreenProps&lt;HomeStackParamList, <span class="hljs-string">'NoteEdit'</span>&gt;;

<span class="hljs-keyword">const</span> availableColors = <span class="hljs-built_in">Object</span>.values(NoteColors);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NoteEdit</span>(<span class="hljs-params">{ navigation }: NoteEditProps</span>) </span>{
  <span class="hljs-keyword">const</span> { selectedNote, updateNote, createNote, deleteNote, updateNoteColor } =
    useNote();
  <span class="hljs-keyword">const</span> input = useRef&lt;TextInput&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> { text, accent } = useTheme().colors;
  <span class="hljs-comment">// debounce title change</span>
  <span class="hljs-keyword">const</span> [nextTitle, setNextTitle] = useState&lt;string | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-comment">// trigger change 300ms after last nextTitle update</span>
  <span class="hljs-keyword">const</span> debouncedTitle = useDebounce&lt;string | <span class="hljs-literal">null</span>&gt;(nextTitle, <span class="hljs-number">300</span>);
  <span class="hljs-comment">// debounce content change</span>
  <span class="hljs-keyword">const</span> [nextContent, setNextContent] = useState&lt;string | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-comment">// trigger change 300ms after last nextContent update</span>
  <span class="hljs-keyword">const</span> debouncedContent = useDebounce&lt;string | <span class="hljs-literal">null</span>&gt;(nextContent, <span class="hljs-number">300</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// focus content input on init</span>
    <span class="hljs-comment">// autofocus doesn't work on android</span>
    <span class="hljs-keyword">if</span> (Platform.OS === <span class="hljs-string">'android'</span> &amp;&amp; input.current) {
      input.current?.focus();
    }
  }, []);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// sync our local state with selected note asap</span>
    <span class="hljs-comment">// avoid running if selectedNote not defined</span>
    <span class="hljs-keyword">if</span> (!selectedNote) <span class="hljs-keyword">return</span>;
    setNextTitle(selectedNote.title);
    setNextContent(selectedNote.content);
  }, [selectedNote]);

  <span class="hljs-comment">// effect triggered when debouncedTitle or debouncedContent change</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// check if nextTitle is initiated and has a value !== from previous</span>
    <span class="hljs-comment">// allow to continue even if we have empty strings</span>
    <span class="hljs-keyword">const</span> validTitle =
      nextTitle !== <span class="hljs-literal">null</span> &amp;&amp;
      nextTitle?.trim()?.length &gt;= <span class="hljs-number">0</span> &amp;&amp;
      nextTitle !== selectedNote?.title;
    <span class="hljs-comment">// same thing for nextContent</span>
    <span class="hljs-keyword">const</span> validContent =
      nextContent !== <span class="hljs-literal">null</span> &amp;&amp;
      nextContent?.trim()?.length &gt;= <span class="hljs-number">0</span> &amp;&amp;
      nextContent !== selectedNote?.content;
    <span class="hljs-keyword">if</span> (validTitle || validContent) {
      <span class="hljs-comment">// if it's a new note one of them can be null =&gt; replace by empty string</span>
      <span class="hljs-keyword">let</span> title = nextTitle ?? <span class="hljs-string">''</span>;
      <span class="hljs-keyword">let</span> content = nextContent ?? <span class="hljs-string">''</span>;
      <span class="hljs-comment">// no selected note means it's a new one</span>
      <span class="hljs-keyword">if</span> (!selectedNote) {
        <span class="hljs-keyword">return</span> createNote(title, content);
      }
      updateNote(selectedNote.id, title, content);
    }
  }, [debouncedTitle, debouncedContent]);

  <span class="hljs-keyword">const</span> updateNoteOnTextChange = <span class="hljs-function">(<span class="hljs-params">text: string</span>) =&gt;</span> setNextContent(text);
  <span class="hljs-keyword">const</span> updateNoteOnTitleChange = <span class="hljs-function">(<span class="hljs-params">text: string</span>) =&gt;</span> setNextTitle(text);
  <span class="hljs-keyword">const</span> deleteCurrentNote = <span class="hljs-function">() =&gt;</span> {
    deleteNote(selectedNote!.id);
    navigation.goBack();
  };

  <span class="hljs-keyword">const</span> goBack = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// delete note if completely empty on go back</span>
    <span class="hljs-keyword">if</span> (
      selectedNote &amp;&amp;
      nextTitle?.trim()?.length === <span class="hljs-number">0</span> &amp;&amp;
      nextContent?.trim()?.length === <span class="hljs-number">0</span>
    ) {
      deleteNote(selectedNote.id);
    }
    navigation.goBack();
  };

  <span class="hljs-keyword">const</span> changeNoteColor = <span class="hljs-function">(<span class="hljs-params">color: NoteColors</span>) =&gt;</span> {
    updateNoteColor(selectedNote!.id, color);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span> <span class="hljs-attr">pl</span>=<span class="hljs-string">{3}</span> <span class="hljs-attr">pr</span>=<span class="hljs-string">{3}</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'background'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
        <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'row'</span>
        <span class="hljs-attr">height</span>=<span class="hljs-string">{50}</span>
        <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
        <span class="hljs-attr">marginTop</span>=<span class="hljs-string">{StatusBar.currentHeight</span> || <span class="hljs-attr">40</span>}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Pressable</span>
          <span class="hljs-attr">onPress</span>=<span class="hljs-string">{goBack}</span>
          <span class="hljs-attr">hitSlop</span>=<span class="hljs-string">{16}</span>
          <span class="hljs-attr">android_ripple</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">borderless:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">color:</span> '<span class="hljs-attr">primary</span>' }}
          <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'arrow-left'</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{text}</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Pressable</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledInput</span>
          <span class="hljs-attr">defaultValue</span>=<span class="hljs-string">{selectedNote?.title}</span>
          <span class="hljs-attr">onChangeText</span>=<span class="hljs-string">{updateNoteOnTitleChange}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Enter a cool title here'</span>
          <span class="hljs-attr">placeholderTextColor</span>=<span class="hljs-string">'gray'</span>
          <span class="hljs-attr">color</span>=<span class="hljs-string">'text'</span>
          <span class="hljs-attr">width</span>=<span class="hljs-string">'100%'</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">'100%'</span>
          <span class="hljs-attr">flexShrink</span>=<span class="hljs-string">{1}</span>
          <span class="hljs-attr">fontSize</span>=<span class="hljs-string">{2}</span>
          <span class="hljs-attr">fontWeight</span>=<span class="hljs-string">'900'</span>
          <span class="hljs-attr">mr</span>=<span class="hljs-string">{3}</span>
          <span class="hljs-attr">ml</span>=<span class="hljs-string">{3}</span>
        /&gt;</span>
        {selectedNote &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Pressable</span>
            <span class="hljs-attr">onPress</span>=<span class="hljs-string">{deleteCurrentNote}</span>
            <span class="hljs-attr">hitSlop</span>=<span class="hljs-string">{16}</span>
            <span class="hljs-attr">android_ripple</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">borderless:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">color:</span> '<span class="hljs-attr">primary</span>' }}
            <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.3 : 1 })}
          &gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'trash-2'</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'red'</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">Pressable</span>&gt;</span>
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
      {selectedNote &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
          <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'row'</span>
          <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'space-between'</span>
          <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
          <span class="hljs-attr">height</span>=<span class="hljs-string">{50}</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'grey'</span>&gt;</span>
            {`Created ${formatDistanceToNow(selectedNote.createdAt, {
              addSuffix: true,
            })}`}
          <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'row'</span> <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>&gt;</span>
            {availableColors.map((color) =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">StyledPressable</span>
                <span class="hljs-attr">key</span>=<span class="hljs-string">{color}</span>
                <span class="hljs-attr">onPress</span>=<span class="hljs-string">{()</span> =&gt;</span> changeNoteColor(color)}
                style={({ pressed }) =&gt; ({ opacity: pressed ? 0.8 : 1 })}
                width={30}
                height={30}
                backgroundColor={color}
                borderRadius={15}
                borderWidth={color === selectedNote?.color ? 5 : 0}
                borderColor={color}
                borderStyle='dotted'
                marginLeft={1}
                marginRight={1}
              /&gt;
            ))}
          <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
      )}
      <span class="hljs-tag">&lt;<span class="hljs-name">KeyboardAwareScrollView</span>
        <span class="hljs-attr">bounces</span>=<span class="hljs-string">{false}</span>
        <span class="hljs-attr">contentContainerStyle</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">paddingVertical:</span> <span class="hljs-attr">16</span> }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledInput</span>
          <span class="hljs-attr">ref</span>=<span class="hljs-string">{input}</span>
          <span class="hljs-attr">autoFocus</span>
          <span class="hljs-attr">multiline</span>
          <span class="hljs-attr">scrollEnabled</span>=<span class="hljs-string">{false}</span>
          <span class="hljs-attr">defaultValue</span>=<span class="hljs-string">{selectedNote?.content}</span>
          <span class="hljs-attr">onChangeText</span>=<span class="hljs-string">{updateNoteOnTextChange}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">'Enter your note here'</span>
          <span class="hljs-attr">placeholderTextColor</span>=<span class="hljs-string">'gray'</span>
          <span class="hljs-attr">color</span>=<span class="hljs-string">'text'</span>
        /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">KeyboardAwareScrollView</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">NoteEditBackground</span>
        <span class="hljs-attr">color</span>=<span class="hljs-string">{selectedNote</span> ? <span class="hljs-attr">selectedNote.color</span> <span class="hljs-attr">:</span> '<span class="hljs-attr">primary</span>'}
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>Finally, update your <code>HomeStack.tsx</code> we don't need to pass params to our route anymore:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> type HomeStackParamList = {
  <span class="hljs-attr">Home</span>: <span class="hljs-literal">undefined</span>;
  NoteEdit: <span class="hljs-literal">undefined</span>;
};

<span class="hljs-keyword">const</span> HomeStack = createNativeStackNavigator&lt;HomeStackParamList&gt;();

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomeStackNavigator</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// wrap our navigator in a view to avoid flickering in dark mode</span>
  <span class="hljs-comment">// hide header on every screen</span>
  <span class="hljs-comment">// by default =&gt; go to Home</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'background'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Navigator</span>
        <span class="hljs-attr">initialRouteName</span>=<span class="hljs-string">'Home'</span>
        <span class="hljs-attr">screenOptions</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">headerShown:</span> <span class="hljs-attr">false</span> }}
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Screen</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'Home'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Home}</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Screen</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'NoteEdit'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{NoteEdit}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">HomeStack.Navigator</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>🎉 Your page is now ready to create / update notes !</p>
<p>However, one important thing is missing, you cannot navigate to our <code>NoteEdit</code> page yet, let's take care of this !</p>
<h2 id="heading-create-button">Create button</h2>
<p>This twitter-like button is a convenient way to allow our users to create a new note easily. To style it, we will use our <code>StyledPressable</code> generic component.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/CreateButton.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CreateButton</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> navigation = useNavigation&lt;HomeProps&gt;();
  <span class="hljs-keyword">const</span> { selectNote } = useNote();

  <span class="hljs-keyword">const</span> createNote = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// call this function without parameter will initiate a new note</span>
    selectNote();
    navigation.navigate(<span class="hljs-string">'NoteEdit'</span>);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledPressable</span>
      <span class="hljs-attr">onPress</span>=<span class="hljs-string">{createNote}</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.8 : 1 })}
      position='absolute'
      bottom={3}
      right={3}
      width={60}
      height={60}
      backgroundColor='primary'
      borderRadius={30}
      justifyContent='center'
      alignItems='center'
      borderWidth={0.5}
      borderColor='accent'
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span> <span class="hljs-attr">name</span>=<span class="hljs-string">'plus'</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{40}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'white'</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledPressable</span>&gt;</span></span>
  );
}
</code></pre>
<p>Then add it to our <code>Home</code> page with a <strong>temporary</strong> count of your notes. This allow you to check everything is working as expected by creating new notes and verifying that the count is moving accordingly:</p>
<pre><code class="lang-diff">export default function Home() {
<span class="hljs-addition">+  const { notes } = useNote();</span>
  const { switchTheme } = useContext(ThemeContext);

  return (
    &lt;StyledView
      flex={1}
      justifyContent='center'
      alignItems='center'
      backgroundColor='primary'
    &gt;
      &lt;StyledText color='white'&gt;Hello from Home&lt;/StyledText&gt;
      &lt;StyledText color='secondary' mt={3} onPress={switchTheme}&gt;
        Click to switch theme
      &lt;/StyledText&gt;
<span class="hljs-addition">+      &lt;StyledText color='white' mt={3}&gt;Number of notes: {notes?.length}&lt;/StyledText&gt;</span>
<span class="hljs-addition">+      &lt;CreateButton /&gt;</span>
    &lt;/StyledView&gt;
  );
}
</code></pre>
<p>You should now have something like this :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638970543802/AZa9UsQ5B.jpeg" alt="home_start.jpg" /></p>
<p>🥳 Congrats you just implemented our main logic, the most difficult part is over !</p>
<p>Now let's have fun and give our <code>Home</code> page a better style !</p>
<h2 id="heading-home-page">Home page</h2>
<p>As a reminder, there is what our home page will looks like at the end :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638916889469/kiuVR_9CnZ.jpeg" alt="final_home_page.jpg" /></p>
<p>For this page, we are going to create three more elements : </p>
<ul>
<li>header</li>
<li>masonry layout</li>
<li>card</li>
</ul>
<h3 id="heading-header">Header</h3>
<p>Start by creating a new <code>src/components/Header.tsx</code></p>
<p>In this component, we will only use our generic components to render the desired design, add an icon to update the theme, and a function to open a twitter link on a click on my username. Feel free to play around the code to make it yours. </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/Header.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-keyword">const</span> TWITTER_NAME = <span class="hljs-string">'@dpnick_'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> HEADER_FULL_HEIGHT = <span class="hljs-number">300</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Header</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { isDarkTheme, switchTheme } = useContext(ThemeContext);

  <span class="hljs-keyword">const</span> openTwitter = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> url = <span class="hljs-string">'https://twitter.com/dpnick_'</span>;
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> supported = <span class="hljs-keyword">await</span> Linking.canOpenURL(<span class="hljs-string">'https://twitter.com/dpnick_'</span>);
      <span class="hljs-keyword">if</span> (supported) {
        <span class="hljs-keyword">await</span> Linking.openURL(url);
      } <span class="hljs-keyword">else</span> {
        Alert.alert(<span class="hljs-string">`Don't know how to open this URL: <span class="hljs-subst">${url}</span>`</span>);
      }
    } <span class="hljs-keyword">catch</span> {
      Alert.alert(<span class="hljs-string">'Error opening Twitter'</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
      <span class="hljs-attr">bg</span>=<span class="hljs-string">'primary'</span>
      <span class="hljs-attr">position</span>=<span class="hljs-string">'absolute'</span>
      <span class="hljs-attr">width</span>=<span class="hljs-string">'100%'</span>
      <span class="hljs-attr">height</span>=<span class="hljs-string">{HEADER_FULL_HEIGHT}</span>
      <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span>
      <span class="hljs-attr">borderBottomLeftRadius</span>=<span class="hljs-string">{48}</span>
      <span class="hljs-attr">borderBottomRightRadius</span>=<span class="hljs-string">{48}</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">padding</span>=<span class="hljs-string">{4}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">fontSize</span>=<span class="hljs-string">{6}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'white'</span>&gt;</span>
          Welcome
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Pressable</span>
          <span class="hljs-attr">onPress</span>=<span class="hljs-string">{openTwitter}</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">fontSize</span>=<span class="hljs-string">{6}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'secondary'</span> <span class="hljs-attr">paddingLeft</span>=<span class="hljs-string">{36}</span>&gt;</span>
            {TWITTER_NAME}
          <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Pressable</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
        <span class="hljs-attr">position</span>=<span class="hljs-string">'absolute'</span>
        <span class="hljs-attr">top</span>=<span class="hljs-string">{(StatusBar.currentHeight</span> || <span class="hljs-attr">40</span>) + <span class="hljs-attr">8</span>}
        <span class="hljs-attr">right</span>=<span class="hljs-string">{16}</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Pressable</span>
          <span class="hljs-attr">onPress</span>=<span class="hljs-string">{switchTheme}</span>
          <span class="hljs-attr">hitSlop</span>=<span class="hljs-string">{16}</span>
          <span class="hljs-attr">android_ripple</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">borderless:</span> <span class="hljs-attr">true</span>, <span class="hljs-attr">color:</span> '<span class="hljs-attr">white</span>' }}
          <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.3 : 1 })}
        &gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">{isDarkTheme</span> ? '<span class="hljs-attr">sun</span>' <span class="hljs-attr">:</span> '<span class="hljs-attr">moon</span>'}
            <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span>
            <span class="hljs-attr">color</span>=<span class="hljs-string">'white'</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">Pressable</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now, use it in our <code>Home</code> page to see the result :</p>
<pre><code class="lang-diff">// src/pages/Home.tsx
// ...imports

// allow to extract from our type "HomeStackParamList" params for "Home" route
// will be useful later
<span class="hljs-addition">+export type HomeProps = NativeStackNavigationProp&lt;HomeStackParamList, 'Home'&gt;;</span>

export default function Home() {
  const { notes } = useNote();
<span class="hljs-deletion">-  const { switchTheme } = useContext(ThemeContext);</span>

  return (
    &lt;StyledView
      flex={1}
<span class="hljs-deletion">-     justifyContent='center'</span>
      alignItems='center'
<span class="hljs-deletion">-     backgroundColor='primary'</span>
<span class="hljs-addition">+     backgroundColor='background'</span>
    &gt;
<span class="hljs-deletion">-      &lt;StyledText color='white'&gt;Hello from Home&lt;/StyledText&gt;</span>
<span class="hljs-deletion">-      &lt;StyledText color='secondary' mt={3} onPress={switchTheme}&gt;</span>
<span class="hljs-deletion">-        Click to switch theme</span>
<span class="hljs-deletion">-      &lt;/StyledText&gt;</span>
<span class="hljs-deletion">-        &lt;StyledText color='white' mt={3}&gt;</span>
<span class="hljs-deletion">-          Number of notes: {notes?.length}</span>
<span class="hljs-deletion">-        &lt;/StyledText&gt;</span>
        &lt;CreateButton /&gt;
<span class="hljs-addition">+      &lt;Header /&gt;</span>
    &lt;/StyledView&gt;
  );
}
</code></pre>
<p>Run your app with <code>expo start</code></p>
<p>Yay! Our header looks really nice! Play with the moon / sun icons and make sure your theme still change!</p>
<h3 id="heading-note-card">Note Card</h3>
<p>Each <code>NoteCard</code> component will represent a <code>Note</code> object, so we can start by creating our component as follow:</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/NoteCard.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-comment">// each card will receive the following as props</span>
<span class="hljs-comment">// but can also be styled from parent thanks to the extend</span>
interface NoteCardProps <span class="hljs-keyword">extends</span> StyledViewProps {
  <span class="hljs-attr">note</span>: Note;
  showNoteEdit: <span class="hljs-function">(<span class="hljs-params">id: number, title: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
}

<span class="hljs-comment">// "...props" represent all possible styling props</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NoteCard</span>(<span class="hljs-params">{
  note,
  showNoteEdit,
  ...props
}: NoteCardProps</span>) </span>{
  <span class="hljs-comment">// call navigate function from parent (props)</span>
  <span class="hljs-keyword">const</span> showDetail = <span class="hljs-function">() =&gt;</span> showNoteEdit(note.id, note.title);

  <span class="hljs-comment">// wrap a card with a pressable element to allow navigation on click</span>
  <span class="hljs-comment">// use date-fns method 'formatDistanceToNow' to make our timestamp human-readable</span>
  <span class="hljs-comment">// using our generic component to create the desired style</span>
  <span class="hljs-comment">// 'numberOfLines' allow us to limit the content preview to 8 lines</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Pressable</span>
      <span class="hljs-attr">onPress</span>=<span class="hljs-string">{showDetail}</span>
      <span class="hljs-attr">style</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">pressed</span> }) =&gt;</span> ({ opacity: pressed ? 0.7 : 1 })}
    &gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> {<span class="hljs-attr">...props</span>}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span>
          <span class="hljs-attr">color</span>=<span class="hljs-string">'white'</span>
          <span class="hljs-attr">numberOfLines</span>=<span class="hljs-string">{1}</span>
          <span class="hljs-attr">fontWeight</span>=<span class="hljs-string">'bold'</span>
          <span class="hljs-attr">fontSize</span>=<span class="hljs-string">{2}</span>
        &gt;</span>
          {note.title}
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'accent'</span> <span class="hljs-attr">mb</span>=<span class="hljs-string">{8}</span>&gt;</span>
          {formatDistanceToNow(note.updatedAt, {
            addSuffix: true,
          })}
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'whitesmoke'</span> <span class="hljs-attr">numberOfLines</span>=<span class="hljs-string">{8}</span>&gt;</span>
          {note.content}
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Pressable</span>&gt;</span></span>
  );
}
</code></pre>
<p>Make sure to have at least one note. Then, you can try your component in the <code>Home</code> page like :</p>
<pre><code class="lang-diff">// src/pages/Home.tsx
// ... other imports
import Card from '@components/Card';

export type HomeProps = NativeStackNavigationProp&lt;HomeStackParamList, 'Home'&gt;;

export default function Home() {
<span class="hljs-addition">+  const { notes, selectNote } = useNote();</span>
<span class="hljs-addition">+  const navigation = useNavigation&lt;HomeProps&gt;();</span>
<span class="hljs-addition">+  const [isLoading, setIsLoading] = useState&lt;boolean&gt;(true);</span>
<span class="hljs-addition">+  const [filteredNotes, setFilteredNotes] = useState&lt;Note[]&gt;([]);</span>
<span class="hljs-addition">+  const { primary } = useTheme().colors;</span>

  // allow to set local state in sync
<span class="hljs-addition">+  useEffect(() =&gt; {</span>
    // avoid running it before notes initialized
<span class="hljs-addition">+    if (!notes) return;</span>
<span class="hljs-addition">+    setFilteredNotes(notes);</span>
<span class="hljs-addition">+    setIsLoading(false);</span>
<span class="hljs-addition">+  }, [notes]);</span>

<span class="hljs-addition">+  const showNoteEdit = (id: number) =&gt; {</span>
<span class="hljs-addition">+    selectNote(id);</span>
<span class="hljs-addition">+    navigation.navigate('NoteEdit');</span>
<span class="hljs-addition">+  };</span>

  // display only our note at last index
  return (
    &lt;StyledView flex={1} alignItems='center' backgroundColor='background'&gt;
<span class="hljs-addition">+      &lt;StyledView paddingTop={350} margin={4}&gt;</span>
<span class="hljs-addition">+        {isLoading ? (</span>
<span class="hljs-addition">+          &lt;ActivityIndicator size='large' color={primary} /&gt;</span>
<span class="hljs-addition">+        ) : (</span>
<span class="hljs-addition">+          &lt;NoteCard</span>
<span class="hljs-addition">+            note={filteredNotes[filteredNotes?.length - 1]}</span>
<span class="hljs-addition">+            showNoteEdit={showNoteEdit}</span>
<span class="hljs-addition">+            bg='secondary'</span>
<span class="hljs-addition">+            p={3}</span>
<span class="hljs-addition">+            borderRadius={16}</span>
<span class="hljs-addition">+            mb={2}</span>
<span class="hljs-addition">+          /&gt;</span>
<span class="hljs-addition">+        )}</span>
<span class="hljs-addition">+      &lt;/StyledView&gt;</span>
      &lt;CreateButton /&gt;
      &lt;Header /&gt;
    &lt;/StyledView&gt;
  );
}
</code></pre>
<p>As you can see, we are using <code>isLoading</code> to display a loader to the user while waiting for his data. It prevents the user to wait in front of a white screen. It will be useful when you will have a large list of notes.</p>
<p>You should now have something like :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638970559917/sxiWY4zj4-.jpeg" alt="home_first_note.jpg" /></p>
<p>Try to update / create / delete your note. You should see the last note element created / updated.</p>
<p>Of course we can have several notes, let's create a list to display them all !</p>
<h3 id="heading-masonry-layout">Masonry layout</h3>
<p><em>Masonry layout</em> is a grid system where each item can have a different height. You have surely already seen some on app like Pinterest.</p>
<p>Although <code>FlatList</code> is often a best choice, we are not going to use this time because it prevents having items with different heights for performance purpose.</p>
<p>Let's create our own !</p>
<p>First, add a new component to handle empty list state <code>src/Components/EmptyList.tsx</code>. This component, is pretty simple, it receives an icon name and a message from props and then display it in a view. It will help to get our list component cleaner.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/Components/EmptyList.tsx</span>
<span class="hljs-comment">// ...imports</span>

interface EmptyListProps {
  <span class="hljs-attr">icon</span>: <span class="hljs-string">'ghost'</span> | <span class="hljs-string">'present'</span>;
  message: string;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">EmptyList</span>(<span class="hljs-params">{ message, icon }: EmptyListProps</span>) </span>{
  <span class="hljs-comment">// allow to get values from current theme</span>
  <span class="hljs-comment">// to use it in non-styled component for example (icon in this case)</span>
  <span class="hljs-keyword">const</span> { text } = useTheme().colors;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span> <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span> <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">SimpleLineIcons</span> <span class="hljs-attr">name</span>=<span class="hljs-string">{icon}</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{48}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{text}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'text'</span> <span class="hljs-attr">mt</span>=<span class="hljs-string">{12}</span> <span class="hljs-attr">fontSize</span>=<span class="hljs-string">{2}</span> <span class="hljs-attr">textAlign</span>=<span class="hljs-string">'center'</span>&gt;</span>
        {message}
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>We are now ready to create our list, create a new component <code>src/components/MasonryList.tsx</code> </p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/MasonryList.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-comment">// we pass the list of note as props to our component</span>
interface MasonryListProps {
  <span class="hljs-attr">notes</span>: Note[];
  search: string;
  userHasNotes: boolean;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">compareDateDesc</span>(<span class="hljs-params">first: Note, second: Note</span>) </span>{
  <span class="hljs-keyword">let</span> result = <span class="hljs-number">0</span>;
  <span class="hljs-keyword">if</span> (second.updatedAt &lt; first.updatedAt) {
    result = <span class="hljs-number">-1</span>;
  }
  <span class="hljs-keyword">if</span> (second.updatedAt &gt; first.updatedAt) {
    result = <span class="hljs-number">1</span>;
  }
  <span class="hljs-keyword">return</span> result;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MasonryList</span>(<span class="hljs-params">{ notes, search, userHasNotes }: MasonryListProps</span>) </span>{
  <span class="hljs-comment">// navigation use HomeProps bc this component live in this route</span>
  <span class="hljs-keyword">const</span> navigation = useNavigation&lt;HomeProps&gt;();
  <span class="hljs-keyword">const</span> { selectNote } = useNote();

  <span class="hljs-comment">// function passed to each NoteCard as prop</span>
  <span class="hljs-keyword">const</span> showNoteEdit = <span class="hljs-function">(<span class="hljs-params">id: number</span>) =&gt;</span> {
    <span class="hljs-comment">// select the clicked note in our state (see NoteContext)</span>
    selectNote(id);
    navigation.navigate(<span class="hljs-string">'NoteEdit'</span>);
  };

  <span class="hljs-keyword">if</span> (!userHasNotes) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">EmptyList</span> <span class="hljs-attr">message</span>=<span class="hljs-string">'Start by creating a note 👇'</span> <span class="hljs-attr">icon</span>=<span class="hljs-string">'present'</span> /&gt;</span></span>;
  }
  <span class="hljs-comment">// explanation bellow</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'row'</span>&gt;</span>
      {notes?.length &gt; 0 ? (
        [...Array(2).keys()].map((column) =&gt; {
          return (
            <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
              <span class="hljs-attr">key</span>=<span class="hljs-string">{column.toString()}</span>
              <span class="hljs-attr">flex</span>=<span class="hljs-string">{1</span> / <span class="hljs-attr">2</span>}
              <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'column'</span>
              <span class="hljs-attr">margin</span>=<span class="hljs-string">{1}</span>
            &gt;</span>
              {notes
                .sort(compareDateDesc)
                .map((note, index) =&gt; {
                  if (index % 2 === column) {
                    return (
                      <span class="hljs-tag">&lt;<span class="hljs-name">NoteCard</span>
                        <span class="hljs-attr">key</span>=<span class="hljs-string">{note.id.toString()}</span>
                        <span class="hljs-attr">note</span>=<span class="hljs-string">{note}</span>
                        <span class="hljs-attr">showNoteEdit</span>=<span class="hljs-string">{showNoteEdit}</span>
                        <span class="hljs-attr">bg</span>=<span class="hljs-string">{note.color}</span>
                        <span class="hljs-attr">p</span>=<span class="hljs-string">{3}</span>
                        <span class="hljs-attr">borderRadius</span>=<span class="hljs-string">{16}</span>
                        <span class="hljs-attr">mb</span>=<span class="hljs-string">{2}</span>
                      /&gt;</span>
                    );
                  }
                  return null;
                })
                .filter((element) =&gt; !!element)}
            <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
          );
        })
      ) : (
        <span class="hljs-tag">&lt;<span class="hljs-name">EmptyList</span>
          <span class="hljs-attr">message</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">No</span> <span class="hljs-attr">corresponding</span> <span class="hljs-attr">result</span> <span class="hljs-attr">for</span> ${<span class="hljs-attr">search</span>}`}
          <span class="hljs-attr">icon</span>=<span class="hljs-string">'ghost'</span>
        /&gt;</span>
      )}
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> memo(MasonryList);
</code></pre>
<p>In a row direction view, we just created two columns. Each one take half of the available space (<code>flex={1 / 2}</code>). Inside each column we iterate over all notes (from props), if the current note index % 2 (number of columns) is equal to the current column we add a <code>Note</code> component to this column otherwise we go directly to the next element.</p>
<p>We also have a two check : </p>
<ul>
<li>if we don't have any <code>notes</code> we display to the user a message to let him know what to do.</li>
<li>if user has notes but none is passed to our component (via props) we display a message to warn the user. Typically, this will be useful for our search feature.</li>
</ul>
<p>You might also have noticed, we sort our notes to display the last updated first, because... who cares about the old notes after all 🤷‍♂️😄</p>
<p>Once it's done, let's try it in our <code>Home</code> page !</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/pages/Home.tsx</span>
<span class="hljs-comment">// ...imports</span>

<span class="hljs-keyword">const</span> StyledScrollView = styled.ScrollView&lt;LayoutProps &amp; SpaceProps&gt;(
  compose(layout, space)
);

<span class="hljs-keyword">export</span> type HomeProps = NativeStackNavigationProp&lt;HomeStackParamList, <span class="hljs-string">'Home'</span>&gt;;

<span class="hljs-keyword">const</span> PADDING_TOP_CONTENT = <span class="hljs-number">16</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { notes } = useNote();
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState&lt;boolean&gt;(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [filteredNotes, setFilteredNotes] = useState&lt;Note[]&gt;([]);
  <span class="hljs-keyword">const</span> { primary } = useTheme().colors;

  <span class="hljs-comment">// allow to set local state in sync</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// avoid running it before notes initialized</span>
    <span class="hljs-keyword">if</span> (!notes) <span class="hljs-keyword">return</span>;
    setFilteredNotes(notes);
    setIsLoading(<span class="hljs-literal">false</span>);
  }, [notes]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span> <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'background'</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledScrollView</span>
        <span class="hljs-attr">width</span>=<span class="hljs-string">'100%'</span>
        <span class="hljs-attr">p</span>=<span class="hljs-string">{16}</span>
        <span class="hljs-attr">pb</span>=<span class="hljs-string">{0}</span>
        <span class="hljs-attr">contentContainerStyle</span>=<span class="hljs-string">{{</span>
          <span class="hljs-attr">paddingTop:</span> <span class="hljs-attr">HEADER_FULL_HEIGHT</span> + <span class="hljs-attr">PADDING_TOP_CONTENT</span>,
        }}
      &gt;</span>
        {isLoading ? (
          <span class="hljs-tag">&lt;<span class="hljs-name">ActivityIndicator</span> <span class="hljs-attr">size</span>=<span class="hljs-string">'large'</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{primary}</span> /&gt;</span>
        ) : (
          <span class="hljs-tag">&lt;<span class="hljs-name">MasonryList</span>
            <span class="hljs-attr">notes</span>=<span class="hljs-string">{filteredNotes}</span>
            <span class="hljs-attr">search</span>=<span class="hljs-string">{</span>''}
            <span class="hljs-attr">userHasNotes</span>=<span class="hljs-string">{!!notes</span> &amp;&amp; <span class="hljs-attr">notes</span>?<span class="hljs-attr">.length</span> &gt;</span> 0}
          /&gt;
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledScrollView</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CreateButton</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>We're getting closer to our final design ! You should now have something like :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1638970576604/HpeyyefSJ.jpeg" alt="home_masonry.jpg" /></p>
<p>As soon as we start having multiple notes, it can be annoying to look for a specific one. This means it's time to implement a search feature !</p>
<h2 id="heading-search-feature">Search feature</h2>
<p>This will be straightforward since everything is already in place.</p>
<p>Create a new component <code>src/components/SearchBar.tsx</code></p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/SearchBar.tsx</span>
<span class="hljs-comment">// ...imports</span>
interface SearchBarInterface {
  <span class="hljs-attr">value</span>: string;
  onChangeText: <span class="hljs-function">(<span class="hljs-params">next: string</span>) =&gt;</span> <span class="hljs-keyword">void</span>;
  placeholder: string;
}

<span class="hljs-keyword">const</span> PADDING_TOP_BAR = <span class="hljs-number">16</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">SearchBar</span>(<span class="hljs-params">{
  value,
  onChangeText,
  placeholder,
}: SearchBarInterface</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
      <span class="hljs-attr">p</span>=<span class="hljs-string">{20}</span>
      <span class="hljs-attr">pt</span>=<span class="hljs-string">{0}</span>
      <span class="hljs-attr">width</span>=<span class="hljs-string">'100%'</span>
      <span class="hljs-attr">position</span>=<span class="hljs-string">'absolute'</span>
      <span class="hljs-attr">borderBottomLeftRadius</span>=<span class="hljs-string">{24}</span>
      <span class="hljs-attr">borderBottomRightRadius</span>=<span class="hljs-string">{24}</span>
      <span class="hljs-attr">top</span>=<span class="hljs-string">{HEADER_FULL_HEIGHT</span> + <span class="hljs-attr">PADDING_TOP_BAR</span>}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
        <span class="hljs-attr">bg</span>=<span class="hljs-string">'accent'</span>
        <span class="hljs-attr">borderRadius</span>=<span class="hljs-string">{8}</span>
        <span class="hljs-attr">flexDirection</span>=<span class="hljs-string">'row'</span>
        <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'flex-start'</span>
        <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
        <span class="hljs-attr">height</span>=<span class="hljs-string">{40}</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">'search'</span>
          <span class="hljs-attr">size</span>=<span class="hljs-string">{20}</span>
          <span class="hljs-attr">color</span>=<span class="hljs-string">'black'</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.iconStart}</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TextInput</span>
          <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.input}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">{placeholder}</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{value}</span>
          <span class="hljs-attr">onChangeText</span>=<span class="hljs-string">{onChangeText}</span>
        /&gt;</span>
        {value?.length &gt; 0 &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Feather</span>
            <span class="hljs-attr">name</span>=<span class="hljs-string">'x'</span>
            <span class="hljs-attr">size</span>=<span class="hljs-string">{24}</span>
            <span class="hljs-attr">color</span>=<span class="hljs-string">'black'</span>
            <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.iconEnd}</span>
            <span class="hljs-attr">onPress</span>=<span class="hljs-string">{()</span> =&gt;</span> onChangeText('')}
          /&gt;
        )}
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> styles = StyleSheet.create({
  <span class="hljs-attr">iconStart</span>: {
    <span class="hljs-attr">marginLeft</span>: <span class="hljs-number">8</span>,
  },
  <span class="hljs-attr">iconEnd</span>: {
    <span class="hljs-attr">marginRight</span>: <span class="hljs-number">8</span>,
  },
  <span class="hljs-attr">input</span>: {
    <span class="hljs-attr">flex</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">fontSize</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">marginLeft</span>: <span class="hljs-number">8</span>,
    <span class="hljs-attr">paddingVertical</span>: <span class="hljs-number">4</span>,
  },
});
</code></pre>
<p>Finally, add it to our <code>Home</code> page</p>
<pre><code class="lang-diff">// src/pages/Home.tsx
// ...imports

const StyledScrollView = styled.ScrollView&lt;LayoutProps &amp; SpaceProps&gt;(
  compose(layout, space)
);

export type HomeProps = NativeStackNavigationProp&lt;HomeStackParamList, 'Home'&gt;;

<span class="hljs-addition">+const SEARCH_INPUT_HEIGHT = 40;</span>
const PADDING_TOP_CONTENT = 16;

export default function Home() {
  const { notes } = useNote();
  const [isLoading, setIsLoading] = useState&lt;boolean&gt;(true);
  const [filteredNotes, setFilteredNotes] = useState&lt;Note[]&gt;([]);
<span class="hljs-addition">+ const [search, setSearch] = useState&lt;string&gt;('');</span>
<span class="hljs-addition">+ const debouncedSearch = useDebounce&lt;string&gt;(search, 500);</span>
  const { primary } = useTheme().colors;

  // allow to set local state in sync
  useEffect(() =&gt; {
    // avoid running it before notes initialized
    if (!notes) return;
    setFilteredNotes(notes);
    setIsLoading(false);
<span class="hljs-addition">+    setSearch('');</span>
  }, [notes]);

  // run each time debouncedSearch change
<span class="hljs-addition">+  useEffect(() =&gt; {</span>
<span class="hljs-addition">+    if (!notes) return;</span>
<span class="hljs-addition">+    const nextData = notes.filter((note) =&gt;</span>
<span class="hljs-addition">+      note.title.toLocaleLowerCase().includes(search.toLocaleLowerCase())</span>
<span class="hljs-addition">+    );</span>
<span class="hljs-addition">+    setFilteredNotes(nextData);</span>
<span class="hljs-addition">+  }, [debouncedSearch]);</span>

  return (
    &lt;StyledView flex={1} alignItems='center' backgroundColor='background'&gt;
      &lt;StyledScrollView
        width='100%'
        p={16}
        pb={0}
        contentContainerStyle={{
          paddingTop:
<span class="hljs-addition">+            HEADER_FULL_HEIGHT + SEARCH_INPUT_HEIGHT + PADDING_TOP_CONTENT,</span>
        }}
      &gt;
        {isLoading ? (
          &lt;ActivityIndicator size='large' color={primary} /&gt;
        ) : (
          &lt;MasonryList
            notes={filteredNotes}
<span class="hljs-deletion">-             search={''}</span>
<span class="hljs-addition">+            search={search}</span>
            userHasNotes={!!notes &amp;&amp; notes?.length &gt; 0}
          /&gt;
        )}
      &lt;/StyledScrollView&gt;
<span class="hljs-addition">+      {notes &amp;&amp; notes?.length &gt; 0 &amp;&amp; (</span>
<span class="hljs-addition">+        &lt;SearchBar</span>
<span class="hljs-addition">+          value={search}</span>
<span class="hljs-addition">+          onChangeText={setSearch}</span>
<span class="hljs-addition">+          placeholder='Search a title...'</span>
<span class="hljs-addition">+        /&gt;</span>
<span class="hljs-addition">+      )}</span>
      &lt;CreateButton /&gt;
      &lt;Header /&gt;
    &lt;/StyledView&gt;
  );
}
</code></pre>
<p>Create a few more notes and try your search feature. </p>
<p>You made it!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we see how to manage your app logic and design to provide your users a nice-looking and using tool. We created a note-taking app but the most important thing is that the concept of global state your learned can be apply to any application.</p>
<p>Next time, in the last article of this series, I will show you how to bring life to this application using animations (reanimated 2).</p>
<p>Congratulations for following until the end! Do not hesitate to subscribe to my newsletters and to join me on <a target="_blank" href="https://twitter.com/dpnick_">twitter</a> to follow my daily tips and tricks.</p>
<p>See you soon! 👋</p>
<p>- Yannick</p>
]]></content:encoded></item><item><title><![CDATA[How to efficiently set up your react native project]]></title><description><![CDATA[Creating a new app is a long and complex process that discourages many developers. Keeping your code simple and maintainable are the keys to give your project a better chance of success. In this series, I will show you my way to implement this throug...]]></description><link>https://code-with-yannick.com/how-to-set-up-your-react-native-project</link><guid isPermaLink="true">https://code-with-yannick.com/how-to-set-up-your-react-native-project</guid><category><![CDATA[React Native]]></category><category><![CDATA[Expo]]></category><category><![CDATA[styled-components]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Mon, 29 Nov 2021 15:29:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638916213251/TAF0HhOaL.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Creating a new app is a long and complex process that discourages many developers. Keeping your code <strong><em>simple</em></strong> and <strong><em>maintainable</em></strong> are the keys to give your project a better chance of success. In this series, I will show you my way to implement this through a complete step-by-step example.</p>
<blockquote>
<p>Disclaimer: This series is composed of three articles. Each part will focus on a different aspect of app development.</p>
</blockquote>
<p>This first article of the series is dedicated to the project configuration. Too many times, developers (I was like that too 🤫), do not take the time to make a good project configuration and start coding too quickly. Experience taught me that it is better to spend time at the beginning setting up a pertinent and scalable configuration. In many ways, this will prevent misconceptions, coding errors, rework and other troubles that can happen along the way.</p>
<p>At the end of this series, you will be able to create your own high-quality app with a solid project configuration, consistent design system, data manipulations, and animations.</p>
<p>If you get stuck, you can find a link to my github repo at the end of this article. You can also leave a comment, I will be happy to answer.</p>
<h2 id="heading-what-are-we-going-to-build">What are we going to build?</h2>
<p>If like me, you like to learn by doing this tutorial is for you. Before starting, I suggest you to read it once to understand the main concern and then start coding at the same time.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/dpnick_/status/1462767755850133509">https://twitter.com/dpnick_/status/1462767755850133509</a></div>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This tutorial is beginner-friendly, however, to follow it flawlessly you should already have some react native and expo basics. If not please read the getting started doc for <a target="_blank" href="https://reactnative.dev/docs/0.64/getting-started">react native</a> and <a target="_blank" href="https://docs.expo.dev/get-started/create-a-new-app/">expo</a> first. If you have any questions, leave a comment or send me a message on <a target="_blank" href="https://twitter.com/dpnick_">Twitter</a>.</p>
<h2 id="heading-create-an-expo-project">Create an expo project</h2>
<p>Open your favorite terminal and let's get started!</p>
<p>If you haven't already, install <strong>expo-cli</strong> by running :</p>
<pre><code class="lang-bash">npm install --global expo-cli
</code></pre>
<p>Then, go ahead and create a new expo project :</p>
<pre><code class="lang-bash">expo init react-native-note &amp;&amp; <span class="hljs-built_in">cd</span> react-native-note
</code></pre>
<p>(you can choose another name if you prefer 😄)</p>
<p>At the question <code>choose a template</code> choose <code>blank (TypeScript)</code></p>
<blockquote>
<p>Typescript is helpful to prevent development errors and so is a huge step in the right direction to become more scalable and productive.</p>
</blockquote>
<p>Congrats your project is created!</p>
<p>Try to run it with <code>expo start</code> to ensure everything is working properly.</p>
<h2 id="heading-configuring-typescript">Configuring typescript</h2>
<p>Open your favorite IDE, in my case (like almost everyone) I will use VS Code.</p>
<blockquote>
<p>Mac pro tip: <code>CMD + P</code> allow searching for a file in the current project</p>
</blockquote>
<p>At the root level of the project, find a file called <code>tsconfig.json</code> and past the content bellow inside :</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"extends"</span>: <span class="hljs-string">"expo/tsconfig.base"</span>,
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"isolatedModules"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"allowSyntheticDefaultImports"</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Allow default imports from modules without default export</span>
    <span class="hljs-attr">"jsx"</span>: <span class="hljs-string">"react-native"</span>, <span class="hljs-comment">// Specify JSX code generation</span>
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"moduleResolution"</span>: <span class="hljs-string">"node"</span>, <span class="hljs-comment">// Specify module resolution strategy</span>
    <span class="hljs-attr">"noEmit"</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// not emit outputs</span>
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"esnext"</span>, <span class="hljs-comment">// Specify ECMAScript target version</span>
    <span class="hljs-attr">"baseUrl"</span>: <span class="hljs-string">"."</span>,
    <span class="hljs-comment">// create paths to our futur modules</span>
    <span class="hljs-attr">"paths"</span>: {
      <span class="hljs-attr">"@components/*"</span>: [<span class="hljs-string">"./src/components/*"</span>],
      <span class="hljs-attr">"@navigation/*"</span>: [<span class="hljs-string">"./src/navigation/*"</span>],
      <span class="hljs-attr">"@contexts/*"</span>: [<span class="hljs-string">"./src/contexts/*"</span>],
      <span class="hljs-attr">"@models/*"</span>: [<span class="hljs-string">"./src/models/*"</span>],
      <span class="hljs-attr">"@pages/*"</span>: [<span class="hljs-string">"./src/pages/*"</span>]
    }
  },
  <span class="hljs-attr">"include"</span>: [<span class="hljs-string">"./src/**/*"</span>]
}
</code></pre>
<h2 id="heading-project-architecture">Project architecture</h2>
<p>By default, you will have this project architecture :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637854945822/rf30U08kF.png" alt="default_architecture.png" /></p>
<p>From here, react native doesn't impose any architecture for your code, technically you could create your components directly at the root. However, it is a good practice to organize your code by concern to improve readability, reusability and debugging.</p>
<p>Let's create some folders. </p>
<blockquote>
<p>VS Code tips: you can create parent and child folders at once by specifying their relative path. For example from our root click <code>new folder</code> and input <code>src/pages</code> this will create both <code>src</code> and <code>pages</code> folders (works also with files).</p>
</blockquote>
<p>At the root of our project, create an <code>src</code> folder where will live your app. Inside <code>src</code>, create multiple folders called <code>pages</code> (no need to specify what is inside I guess 😅), <code>components</code> (for child components), hooks, contexts, navigation, and models (for typescript interfaces).</p>
<h2 id="heading-async-storage">Async storage</h2>
<p>We will use async storage to persist user preferences like the selected theme. Install it with the following command :</p>
<pre><code class="lang-bash">expo install @react-native-async-storage/async-storage
</code></pre>
<p>Add a file <code>src/models/storage.ts</code>, where we will define our storage keys :</p>
<blockquote>
<p>using an enum prevent typos</p>
</blockquote>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> enum StorageKeys {
  USE_DARK_THEME = <span class="hljs-string">'useDarkTheme'</span>
}
</code></pre>
<h2 id="heading-design-system">Design system</h2>
<h3 id="heading-dependencies">Dependencies</h3>
<p>Defining your design system is one of the most important steps to give your app consistent and neat user visual effects. To handle it in the best way, we will use <code>styled-components</code>, this CSS-in-JS framework provides a simple and flexible way to create react components with the ability to define <em>style based on component props</em>. <a target="_blank" href="https://styled-components.com/docs/basics#react-native">Link to documentation</a></p>
<pre><code class="lang-bash">npm i styled-system styled-components &amp;&amp; npm i --save-dev @types/styled-system @types/styled-components @types/styled-components-react-native babel-plugin-styled-components babel-plugin-module-resolver
</code></pre>
<p>and update your <code>babel.config.js</code> file as follow :</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">api</span>) </span>{
  api.cache(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">return</span> {
    <span class="hljs-attr">presets</span>: [<span class="hljs-string">'babel-preset-expo'</span>],
    <span class="hljs-attr">plugins</span>: [
      <span class="hljs-string">'babel-plugin-styled-components'</span>,
      [
        <span class="hljs-string">'module-resolver'</span>,
        {
          <span class="hljs-attr">root</span>: [<span class="hljs-string">'./src'</span>],
          <span class="hljs-attr">extensions</span>: [<span class="hljs-string">'.ios.js'</span>, <span class="hljs-string">'.android.js'</span>, <span class="hljs-string">'.js'</span>, <span class="hljs-string">'.ts'</span>, <span class="hljs-string">'.tsx'</span>, <span class="hljs-string">'.json'</span>],
          <span class="hljs-attr">alias</span>: {
            <span class="hljs-string">'@components'</span>: <span class="hljs-string">'./src/components'</span>,
            <span class="hljs-string">'@navigation'</span>: <span class="hljs-string">'./src/navigation'</span>,
            <span class="hljs-string">'@contexts'</span>: <span class="hljs-string">'./src/contexts'</span>,
            <span class="hljs-string">'@models'</span>: <span class="hljs-string">'./src/models'</span>,
            <span class="hljs-string">'@pages'</span>: <span class="hljs-string">'./src/pages'</span>,
          },
        },
      ],
    ],
  };
};
</code></pre>
<blockquote>
<p>You may have noticed the aliases, you need to configure them both in typescript (we did it above) and babel. Thanks to it, instead of using absolute imports you can use our aliases. For example <code>import SomeComponent from './src/components/SomeComponent'</code> will be <code>import SomeComponent from '@components/SomeComponent'</code></p>
</blockquote>
<p>Run your app with <code>expo start -c</code> to clear your cache and take our changes into consideration.</p>
<p>Styled components has its own syntax, creating a simple full size centered <code>View</code> with background color would be :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// simple example of styled-components</span>
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>

<span class="hljs-keyword">const</span> StyledView = styled.View<span class="hljs-string">`
  background-color: <span class="hljs-subst">${(props) =&gt; props.color}</span>;
  justify-content: center;
  align-items: center;
  flex: 1;
`</span>;

<span class="hljs-comment">// then use it like any other component :</span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">color</span>=<span class="hljs-string">”red”</span>&gt;</span>{...}<span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
</code></pre>
<p>In addition to <code>styled-components</code>, we will take advantage of <code>styled-system</code>. This library will extend <code>styled-components</code> improving our style structure by adding <em>styling props directly to your react component</em>. <code>styled-system</code> allow also to control styles based on a global theme object (scales, colors, layouts...). This will upgrade both our design consistency and our code reusability and then will allow us to iterate quickly. <a target="_blank" href="https://styled-system.com/">Link to documentation</a></p>
<p>For reference, the same component as above using <code>styled-system</code> will be :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// simple example of styled-components and styled-system</span>
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>
<span class="hljs-keyword">import</span> { 
  compose,
  color,
  ColorProps,
  flex,
  flexbox,
  FlexboxProps,
  FlexProps 
} <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-system'</span>

<span class="hljs-comment">// get our component typed and enjoy autocompletion</span>
type StyledViewProps = ColorProps &amp; FlexProps &amp; FlexboxProps;

<span class="hljs-keyword">const</span> StyledView = styled.View&lt;StyledViewProps&gt;(
  compose(
    color,
    flex,
    flexBox
  )
);
<span class="hljs-comment">// then use it like :</span>
<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
  <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span>
  <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span>
  <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
  <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'red'</span>
&gt;</span>{...}<span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
</code></pre>
<p>Pretty easy to read right?</p>
<blockquote>
<p>you can even shorten the syntax: <code>backgroundColor</code> become <code>bg</code>, <code>margin</code> become <code>m</code>... but I wanted to keep it simple to understand.</p>
</blockquote>
<p>But wait, the real magic happens when you start using theming!</p>
<h3 id="heading-theming">Theming</h3>
<p>Inside <code>src/models</code>, create a file named <code>styled.d.ts</code>. This file will extend the empty <code>DefaultTheme</code> interface provided by <code>styled-components</code> to contain our custom theme :</p>
<pre><code><span class="hljs-comment">// src/models/style.d.ts</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'styled-components'</span>;

<span class="hljs-keyword">declare</span> <span class="hljs-keyword">module</span> 'styled-components' {
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> DefaultTheme {
    fontSizes: <span class="hljs-built_in">number</span>[];
    space: <span class="hljs-built_in">number</span>[];
    colors: {
      primary: <span class="hljs-built_in">string</span>;
      secondary: <span class="hljs-built_in">string</span>;
      background: <span class="hljs-built_in">string</span>;
      text: <span class="hljs-built_in">string</span>;
      accent: <span class="hljs-built_in">string</span>;
    };
  }
}
</code></pre><p>Then continue by creating a <code>Theme.ts</code> into your <code>src</code> folder and paste the following content inside :</p>
<pre><code class="lang-js"><span class="hljs-comment">// src/Theme.tsx</span>
<span class="hljs-keyword">import</span> { DefaultTheme } <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;

<span class="hljs-comment">// you can play with the colors here</span>
<span class="hljs-keyword">const</span> lightTheme: DefaultTheme = {
  <span class="hljs-attr">fontSizes</span>: [<span class="hljs-number">12</span>, <span class="hljs-number">14</span>, <span class="hljs-number">16</span>, <span class="hljs-number">20</span>, <span class="hljs-number">24</span>, <span class="hljs-number">32</span>, <span class="hljs-number">36</span>], <span class="hljs-comment">// could also be defined as em</span>
  <span class="hljs-attr">space</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>, <span class="hljs-number">16</span>, <span class="hljs-number">32</span>, <span class="hljs-number">64</span>],
  <span class="hljs-attr">colors</span>: {
    <span class="hljs-attr">primary</span>: <span class="hljs-string">'#1A1B41'</span>,
    <span class="hljs-attr">secondary</span>: <span class="hljs-string">'#F4845F'</span>,
    <span class="hljs-attr">background</span>: <span class="hljs-string">'#FEFEFF'</span>,
    <span class="hljs-attr">text</span>: <span class="hljs-string">'black'</span>,
    <span class="hljs-attr">accent</span>: <span class="hljs-string">'#d9dbda'</span>,
  },
};

<span class="hljs-keyword">const</span> darkTheme: DefaultTheme = {
  <span class="hljs-attr">fontSizes</span>: [<span class="hljs-number">12</span>, <span class="hljs-number">14</span>, <span class="hljs-number">16</span>, <span class="hljs-number">20</span>, <span class="hljs-number">24</span>, <span class="hljs-number">32</span>, <span class="hljs-number">36</span>],
  <span class="hljs-attr">space</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>, <span class="hljs-number">16</span>, <span class="hljs-number">32</span>, <span class="hljs-number">64</span>],
  <span class="hljs-attr">colors</span>: {
    <span class="hljs-attr">primary</span>: <span class="hljs-string">'#7FC29B'</span>,
    <span class="hljs-attr">secondary</span>: <span class="hljs-string">'#334756'</span>,
    <span class="hljs-attr">background</span>: <span class="hljs-string">'#272838'</span>,
    <span class="hljs-attr">text</span>: <span class="hljs-string">'white'</span>,
    <span class="hljs-attr">accent</span>: <span class="hljs-string">'#d9dbda'</span>,
  },
};

enum Theme {
  LIGHT = <span class="hljs-string">'light'</span>,
  DARK = <span class="hljs-string">'dark'</span>,
}

<span class="hljs-keyword">export</span> { lightTheme, darkTheme, Theme };
</code></pre>
<p>As you can see, we use the <code>DefaultTheme</code> interface we defined just before to create two themes with different values (especially for colors). We also create an enum to identify each theme and we export everything to be able to use it elsewhere. </p>
<p>Now that we have our themes, we will make use of the React context API to make them available across all our components. <code>styled-components</code> provide a context to expose the selected theme, however, we would like to extend this to be able to switch between themes.</p>
<p>To do this, we will wrap the <code>ThemeProvider</code> from <code>styled-components</code>. Inside <code>src/contexts</code> add a file called <code>ThemeContext.tsx</code> :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/contexts/ThemeContext.tsx</span>
<span class="hljs-keyword">import</span> AsyncStorage <span class="hljs-keyword">from</span> <span class="hljs-string">'@react-native-async-storage/async-storage'</span>;
<span class="hljs-keyword">import</span> React, { createContext, useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { Alert, useColorScheme } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>;
<span class="hljs-keyword">import</span> { ThemeProvider <span class="hljs-keyword">as</span> StyledThemeProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;
<span class="hljs-keyword">import</span> { StorageKeys } <span class="hljs-keyword">from</span> <span class="hljs-string">'@models/storage'</span>;
<span class="hljs-keyword">import</span> { darkTheme, lightTheme } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Theme'</span>;

interface IThemeContext {
  <span class="hljs-attr">isDarkTheme</span>: boolean | <span class="hljs-literal">undefined</span>;
  switchTheme: <span class="hljs-function">() =&gt;</span> <span class="hljs-keyword">void</span>;
}

<span class="hljs-comment">// create context with default value</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ThemeContext = createContext&lt;IThemeContext&gt;({
  <span class="hljs-attr">isDarkTheme</span>: <span class="hljs-literal">undefined</span>,
  <span class="hljs-attr">switchTheme</span>: <span class="hljs-function">() =&gt;</span> {},
});

<span class="hljs-keyword">const</span> ThemeProvider = <span class="hljs-function">(<span class="hljs-params">{ children }: { children: React.ReactNode }</span>) =&gt;</span> {
  <span class="hljs-comment">// get from OS user prefered theme</span>
  <span class="hljs-keyword">const</span> colorScheme = useColorScheme();
  <span class="hljs-keyword">const</span> [isDark, setIsDark] = useState&lt;boolean&gt;(<span class="hljs-literal">false</span>);

  <span class="hljs-comment">// on component first render =&gt; check if we have a theme value in async storage</span>
  <span class="hljs-comment">// otherwise =&gt; take user prefered theme</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> setUserTheme = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> useDarkMode = <span class="hljs-keyword">await</span> AsyncStorage.getItem(StorageKeys.USE_DARK_THEME);
        <span class="hljs-keyword">let</span> result = colorScheme === <span class="hljs-string">'dark'</span>;
        <span class="hljs-keyword">if</span> (useDarkMode !== <span class="hljs-literal">null</span>) {
          result = <span class="hljs-built_in">JSON</span>.parse(useDarkMode);
        }
        setIsDark(result);
      } <span class="hljs-keyword">catch</span> {
        Alert.alert(<span class="hljs-string">'An error occured getting your saved theme'</span>);
      }
    };
    setUserTheme();
  }, []);

  <span class="hljs-comment">// update theme &amp; set the new value to async storage</span>
  <span class="hljs-keyword">const</span> switchTheme = <span class="hljs-keyword">async</span> () =&gt; {
    setIsDark(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> !prev);
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> AsyncStorage.setItem(
        StorageKeys.USE_DARK_THEME,
        <span class="hljs-built_in">JSON</span>.stringify(!isDark)
      );
    } <span class="hljs-keyword">catch</span> {
      Alert.alert(<span class="hljs-string">'An error occured while updating theme'</span>);
    }
  };

  <span class="hljs-comment">// wrap `styled-components` provider with our own one</span>
  <span class="hljs-comment">// according to `isDark` provide the corresponding theme from `Theme.ts`</span>
  <span class="hljs-comment">// every `children` will have access to `isDarkTheme`, `switchTheme` and `theme`</span>
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ThemeContext.Provider</span>
      <span class="hljs-attr">value</span>=<span class="hljs-string">{{</span>
        <span class="hljs-attr">isDarkTheme:</span> <span class="hljs-attr">isDark</span>,
        <span class="hljs-attr">switchTheme</span>,
      }}
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledThemeProvider</span> <span class="hljs-attr">theme</span>=<span class="hljs-string">{isDark</span> ? <span class="hljs-attr">darkTheme</span> <span class="hljs-attr">:</span> <span class="hljs-attr">lightTheme</span>}&gt;</span>
        {children}
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledThemeProvider</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ThemeContext.Provider</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ThemeProvider;
</code></pre>
<p>All you have to do now is to wrap all your app within this context. After that, every components can have access to the current theme through the <code>useTheme</code> hook. Even better, our styled components can directly use theme variables in their props. Update your <code>App.tsx</code> to use it :</p>
<blockquote>
<p><code>color</code> or <code>backgroundColor</code> props can now use any colors from our theme (you can still input color that doesn't exist in theme like #6DD8AF or "red" for example).</p>
<p><code>margin</code> <code>padding</code> props use values from "space" array in our theme. For example when you set <code>margin={2}</code> the value "2" represents the index in this array. If you go further than the available indexes, the value will be used as pixels.</p>
<p><code>fontSize</code> use values from "fontSizes" array in our theme. Just like <code>margin</code> and <code>padding</code> it will be interpreted as index in the array. <a target="_blank" href="https://styled-system.com/api">Check the <code>styled-system</code> doc out for more.</a></p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-comment">// App.tsx</span>
<span class="hljs-keyword">import</span> { StatusBar } <span class="hljs-keyword">from</span> <span class="hljs-string">'expo-status-bar'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { SafeAreaView, StyleSheet, Text } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-native'</span>;
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;
<span class="hljs-keyword">import</span> {
  color,
  ColorProps,
  compose,
  flex,
  flexbox,
  FlexboxProps,
  FlexProps,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-system'</span>;
<span class="hljs-keyword">import</span> ThemeProvider <span class="hljs-keyword">from</span> <span class="hljs-string">'@contexts/ThemeContext'</span>;

type StyledViewProps = ColorProps &amp; FlexProps &amp; FlexboxProps;
<span class="hljs-keyword">const</span> StyledView = styled.View&lt;StyledViewProps&gt;(compose(color, flex, flexbox));

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">SafeAreaView</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.container}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ThemeProvider</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
          <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span>
          <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span>
          <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
          <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'primary'</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Text</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{styles.title}</span>&gt;</span>Hello from App<span class="hljs-tag">&lt;/<span class="hljs-name">Text</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">StatusBar</span> <span class="hljs-attr">style</span>=<span class="hljs-string">'auto'</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ThemeProvider</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">SafeAreaView</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">const</span> styles = StyleSheet.create({
  <span class="hljs-attr">container</span>: {
    <span class="hljs-attr">flex</span>: <span class="hljs-number">1</span>,
  },
  <span class="hljs-attr">title</span>: {
    <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
  },
});
</code></pre>
<p>Refresh your app to enjoy your first themed view!</p>
<h3 id="heading-generic-components">Generic components</h3>
<p>To avoid re-declaring the most used components (<code>View</code> and <code>Text</code>) in every page we are going to create generic components to reuse.</p>
<p>In <code>src/components</code> create two components <code>StyledView.tsx</code> and <code>StyledText.tsx</code> :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/components/StyledView.tsx</span>
<span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;
<span class="hljs-keyword">import</span> {
  color,
  ColorProps,
  compose,
  flex,
  flexbox,
  FlexboxProps,
  FlexProps,
  layout,
  LayoutProps,
  position,
  PositionProps,
  space,
  SpaceProps,
  borderRadius,
  BorderRadiusProps
} <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-system'</span>;

<span class="hljs-comment">// space allow to use padding, margin...</span>
<span class="hljs-comment">// layout allow to use width, height...</span>
type StyledViewProps = ColorProps &amp;
  SpaceProps &amp;
  PositionProps &amp;
  FlexProps &amp;
  FlexboxProps &amp;
  LayoutProps &amp;
  BorderRadiusProps;

<span class="hljs-keyword">const</span> StyledView = styled.View&lt;StyledViewProps&gt;(
  compose(color, space, position, flex, flexbox, layout, borderRadius)
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StyledView;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;
<span class="hljs-keyword">import</span> {
  color,
  ColorProps,
  compose,
  space,
  SpaceProps,
  typography,
  TypographyProps,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-system'</span>;

<span class="hljs-comment">// typography allow to change font size, style etc</span>
type StyledTextProps = ColorProps &amp; SpaceProps &amp; TypographyProps;

<span class="hljs-keyword">const</span> StyledText = styled.Text&lt;StyledTextProps&gt;(
  compose(color, space, typography)
);

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> StyledText;
</code></pre>
<p>As you can see, we pass common used style properties to our component via the compose helper function. Thanks to it we will now have access to those properties directly through the component props</p>
<p>Ok, so let's use it!</p>
<h2 id="heading-prepare-navigation">Prepare navigation</h2>
<p>For this app, we are going to use a famous library called <code>React Navigation</code>. It allows to easily handle multiple navigation patterns such as tabs, drawers, stacks...</p>
<blockquote>
<p>In our case, we are going to use a stack. If you want to learn more about other possibilities, leave a comment and I will consider creating a dedicated article.</p>
</blockquote>
<p>Install the library and its dependencies with the following command :</p>
<pre><code class="lang-bash">npm install @react-navigation/native @react-navigation/native-stack &amp;&amp; expo install react-native-screens react-native-safe-area-context
</code></pre>
<p>While installing, create two "placeholder" components respectively named <code>Home.tsx</code> and <code>NoteEdit.tsx</code> inside <code>src/pages</code> folder.</p>
<blockquote>
<p>Pro tips: I'm using the <code>ES7 React/Redux/GraphQL/React-Native snippets</code> extension to speed up the creation process. With it, add a new file and write <code>rnf</code> inside to create a functional component.</p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/pages/Home.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> StyledText <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/StyledText'</span>;
<span class="hljs-keyword">import</span> StyledView <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/StyledView'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { switchTheme } = useContext(ThemeContext);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
      <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span>
      <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span>
      <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
      <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'primary'</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'white'</span>&gt;</span>Hello from Home<span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'secondary'</span> <span class="hljs-attr">mt</span>=<span class="hljs-string">{3}</span> <span class="hljs-attr">onPress</span>=<span class="hljs-string">{switchTheme}</span>&gt;</span>
        Click to switch theme
      <span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/pages/NoteEdit.tsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> StyledText <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/StyledText'</span>;
<span class="hljs-keyword">import</span> StyledView <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/StyledView'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NoteEdit</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span>
      <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span>
      <span class="hljs-attr">justifyContent</span>=<span class="hljs-string">'center'</span>
      <span class="hljs-attr">alignItems</span>=<span class="hljs-string">'center'</span>
      <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'background'</span>
    &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">StyledText</span> <span class="hljs-attr">color</span>=<span class="hljs-string">'text'</span>&gt;</span>Hello from NoteEdit<span class="hljs-tag">&lt;/<span class="hljs-name">StyledText</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<blockquote>
<p>With these two components, you get a first glimpse of why a good project setup could improve your productivity. Our components are much more readable and concise.</p>
</blockquote>
<p>Once it's done, create a file <code>src/navigation/HomeStack.tsx</code>. In this file, we are going to create our main stack navigator with our two screens.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/navigation/HomeStack.tsx</span>
<span class="hljs-keyword">import</span> { createNativeStackNavigator } <span class="hljs-keyword">from</span> <span class="hljs-string">'@react-navigation/native-stack'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useTheme } <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components/native'</span>;
<span class="hljs-keyword">import</span> { Note } <span class="hljs-keyword">from</span> <span class="hljs-string">'@models/note'</span>;
<span class="hljs-keyword">import</span> StyledView <span class="hljs-keyword">from</span> <span class="hljs-string">'@components/StyledView'</span>;
<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">'@pages/Home'</span>;
<span class="hljs-keyword">import</span> NoteEdit <span class="hljs-keyword">from</span> <span class="hljs-string">'@pages/NoteEdit'</span>;

<span class="hljs-comment">// type navigation + each route params</span>
<span class="hljs-keyword">export</span> type HomeStackParamList = {
  <span class="hljs-attr">Home</span>: <span class="hljs-literal">undefined</span>;
  NoteEdit: { <span class="hljs-attr">id</span>: number; title: string };
};

<span class="hljs-comment">// using type avoid typo in route name by providing autocomplete</span>
<span class="hljs-keyword">const</span> HomeStack = createNativeStackNavigator&lt;HomeStackParamList&gt;();

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">HomeStackNavigator</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { background, text } = useTheme().colors;
  <span class="hljs-comment">// by default =&gt; go to Home</span>
  <span class="hljs-comment">// wrapping everything in a view with bg color avoid</span>
  <span class="hljs-comment">// white flickering in dark mode</span>
  <span class="hljs-keyword">return</span> (
   <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StyledView</span> <span class="hljs-attr">flex</span>=<span class="hljs-string">{1}</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">'background'</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Navigator</span> <span class="hljs-attr">initialRouteName</span>=<span class="hljs-string">"Home"</span> &gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Screen</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">'Home'</span>
        <span class="hljs-attr">component</span>=<span class="hljs-string">{Home}</span>
        <span class="hljs-attr">options</span>=<span class="hljs-string">{()</span> =&gt;</span> ({
          headerShown: false,
        })}
      /&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">HomeStack.Screen</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">'NoteEdit'</span>
        <span class="hljs-attr">component</span>=<span class="hljs-string">{NoteEdit}</span>
        <span class="hljs-attr">options</span>=<span class="hljs-string">{({</span> <span class="hljs-attr">route</span> }) =&gt;</span> ({
          headerStyle: { backgroundColor: background },
          headerTintColor: text,
          headerTitle: route.params.title,
        })}
      /&gt;
    <span class="hljs-tag">&lt;/<span class="hljs-name">HomeStack.Navigator</span>&gt;</span>
   <span class="hljs-tag">&lt;/<span class="hljs-name">StyledView</span>&gt;</span></span>
  );
}
</code></pre>
<p>And then add an <code>src/navigation/index.tsx</code> where we will create our main navigation container :</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// src/navigation/index.tsx</span>
<span class="hljs-keyword">import</span> { NavigationContainer } <span class="hljs-keyword">from</span> <span class="hljs-string">'@react-navigation/native'</span>;
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> HomeStackNavigator <span class="hljs-keyword">from</span> <span class="hljs-string">'.@navigation/HomeStack'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Navigation</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">NavigationContainer</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">HomeStackNavigator</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">NavigationContainer</span>&gt;</span></span>
  );
}
</code></pre>
<p>Finally, we need to use this component in our <code>App.tsx</code></p>
<pre><code class="lang-diff">import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { SafeAreaView, StyleSheet } from 'react-native';
import ThemeProvider from '@contexts/ThemeContext';
import Navigation from '@navigation/index';
<span class="hljs-deletion">-import {</span>
<span class="hljs-deletion">-  color,</span>
<span class="hljs-deletion">-  ColorProps,</span>
<span class="hljs-deletion">-  compose,</span>
<span class="hljs-deletion">-  flex,</span>
<span class="hljs-deletion">-  flexbox,</span>
<span class="hljs-deletion">-  FlexboxProps,</span>
<span class="hljs-deletion">-  FlexProps,</span>
<span class="hljs-deletion">-} from 'styled-system';</span>

<span class="hljs-deletion">-type StyledViewProps = ColorProps &amp; FlexProps &amp; FlexboxProps;</span>
<span class="hljs-deletion">-const StyledView = styled.View&lt;StyledViewProps&gt;(compose(color, flex, flexbox));</span>

export default function App() {
  return (
    &lt;SafeAreaView style={styles.container}&gt;
      &lt;ThemeProvider&gt;
<span class="hljs-deletion">-        &lt;StyledView</span>
<span class="hljs-deletion">-          flex={1}</span>
<span class="hljs-deletion">-          justifyContent='center'</span>
<span class="hljs-deletion">-          alignItems='center'</span>
<span class="hljs-deletion">-          backgroundColor='primary'</span>
<span class="hljs-deletion">-        &gt;</span>
<span class="hljs-deletion">-          &lt;Text style={styles.title}&gt;Hello from App&lt;/Text&gt;</span>
<span class="hljs-deletion">-        &lt;/StyledView&gt;</span>
<span class="hljs-addition">+       &lt;Navigation /&gt;</span>
        &lt;StatusBar style='auto' /&gt;
      &lt;/ThemeProvider&gt;
    &lt;/SafeAreaView&gt;
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
<span class="hljs-deletion">-  title: {</span>
<span class="hljs-deletion">-    color: 'white',</span>
<span class="hljs-deletion">-  },</span>
});
</code></pre>
<p>Run your app, you should see your <code>Home</code> page, try to click on the "click to switch theme". Congrats you successfully setup your project!</p>
<p>Your favorite theme is even persisted in your mobile storage 🤗</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This part wasn't the funniest, but having all of this already configured will really be a game changer for the rest of the tutorial. This will allow us to iterate quickly while keeping our code clean.</p>
<p>In the next part, we will continue by building our pages, components and business logic. 
We will see among other things how to create a masonry layout and a search feature.</p>
<p>I hope you enjoy and learn something interesting with this tutorial. If you have any suggestion / question feel free to leave a comment, I will be happy to answer it.</p>
<p>Subscribe to my newsletter to get notified when the next article comes out!</p>
<p><a target="_blank" href="https://github.com/dpnick/react-native-note-tutorial">Link to my repo</a></p>
<p>See you soon! 👋</p>
<p>- Yannick </p>
]]></content:encoded></item><item><title><![CDATA[Who I am & what's this blog purpose]]></title><description><![CDATA[Who I am?
Hey 👋 My name is Yannick, I’m living in Dijon, the city of wine and mustard, in eastern France. I have been a full-stack javascript developer for over 5 years now. I’m currently working as a front-end developer for a smart clothes start-up...]]></description><link>https://code-with-yannick.com/who-i-am</link><guid isPermaLink="true">https://code-with-yannick.com/who-i-am</guid><category><![CDATA[introduction]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Yannick Mougin]]></dc:creator><pubDate>Fri, 19 Nov 2021 12:08:49 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1638199555250/MDiGmSaio.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-who-i-am">Who I am?</h2>
<p>Hey 👋 My name is Yannick, I’m living in Dijon, the city of wine and mustard, in eastern France. I have been a full-stack javascript developer for over 5 years now. I’m currently working as a front-end developer for a smart clothes start-up.</p>
<p>I like to create performant user-engaging apps. I am always working on side projects to improve my skills. Lately, I released a mobile app called <strong><em>agame</em></strong> which allows users to find new video games based on their preferences (if you want to check it out I will include links at the end of this article).</p>
<p>Enough about me, let’s talk about this blog!</p>
<h2 id="heading-what-to-expect-from-this-blog">What to expect from this blog ?</h2>
<p>Although I have been coding for a while, I still enjoy learning and experimenting with new concepts/technologies. On this blog, you can follow me along my coding journey (especially but not only) around the javascript ecosystem.</p>
<p>You can also expect me to share my best tips and what makes my life as developer easier.</p>
<h2 id="heading-main-subjects">Main subjects</h2>
<p>I planned to talk about multiple subjects including :</p>
<ul>
<li>Technologies (React native / react / Node.js / Next.js / Angular / Vanilla js)</li>
<li>Best practices / performance optimization</li>
<li>Tutorials for creating smooth app (animations etc)</li>
<li>Experiments (like creating 3D app with three.js)</li>
<li>What I learned from my experiences</li>
</ul>
<p>And many more things…</p>
<p>If you have any idea / topic you would like me to talk about, feel free to leave a comment or to join me on Twitter.</p>
<blockquote>
<p>Bonus: the first articles will be a short serie explaining how to create a powerful/maintainable and engaging mobile application.</p>
</blockquote>
<p>Stay tuned!</p>
<p>- Yannick</p>
<p>Agame links:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://apps.apple.com/fr/app/agame/id1563935125">https://apps.apple.com/fr/app/agame/id1563935125</a></div>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://play.google.com/store/apps/details?id=com.mougin.agame&amp;hl=en&amp;gl=US">https://play.google.com/store/apps/details?id=com.mougin.agame&amp;hl=en&amp;gl=US</a></div>
]]></content:encoded></item></channel></rss>