<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Janic Duplessis on Medium]]></title>
        <description><![CDATA[Stories by Janic Duplessis on Medium]]></description>
        <link>https://medium.com/@janicduplessis?source=rss-a0efb5e07003------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*0tamEJWcn1bBGywdHgkPsg.jpeg</url>
            <title>Stories by Janic Duplessis on Medium</title>
            <link>https://medium.com/@janicduplessis?source=rss-a0efb5e07003------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 14 Apr 2026 10:47:52 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@janicduplessis/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Help your users share their love for your app with React Native]]></title>
            <link>https://blog.expo.dev/help-your-users-share-their-love-for-your-app-with-react-native-7549b1b0b3af?source=rss-a0efb5e07003------2</link>
            <guid isPermaLink="false">https://medium.com/p/7549b1b0b3af</guid>
            <category><![CDATA[referral-marketing]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[expo]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Janic Duplessis]]></dc:creator>
            <pubDate>Thu, 08 Aug 2019 18:56:22 GMT</pubDate>
            <atom:updated>2019-08-08T18:56:22.037Z</atom:updated>
            <content:encoded><![CDATA[<p>You’ve spent all this time building this great app but now you have to find a way to share it with others. A good way to do this is with a referral screen that lets users share a link to all of their contacts. I built this recently for my startup <a href="https://th3rdwave.app.link/j9IkRHpyLR">Th3rd Wave</a> and I figured this is something that can apply to most apps so I’ll share the story and code here.</p><p>React Native has a Share module to use the platform share functionality, but in this case we want to send the link to the user’s contacts so that’s not what we want to use. App platforms provide the primitives needed to build this experience but there is still a lot of work involved to build the UI and tie it all together.</p><h3>Let’s get to work</h3><p>Here’s the flow we’re looking to build.</p><ul><li>Ask permissions to access contacts.</li><li>Fetch all contacts.</li><li>Use SectionList to group contacts alphabetically. Also some contacts might appear more than once if they contain multiple emails or phone numbers.</li><li>Open the email composer with selected email contacts.</li><li>Open the SMS composer with selected phone number contacts.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*SkobumaWC3vfJrsQmnQEZw.png" /><figcaption>Final result</figcaption></figure><p>We won’t bother too much with UI here, as your app probably has its own design system.</p><h3>JavaScript Ninja</h3><p>Like any great JS dev let’s look at what exists on NPM to be able to accomplish this. First, we need contacts, and the first thing that pops up is <a href="https://github.com/rt2zz/react-native-contacts">react-native-contacts</a>, which actually looks great: 1k stars, recent commits, we’re in business. But then comes the fun part…</p><p>iOS has something called MFMailComposeViewController and MFMessageComposeViewController which allow sending emails or text without leaving the app. This means we cannot just use Linking.openURL(&#39;mailto:&#39;) since it would open the mail app and lead to worse UX.</p><p>So back to Google, but this time there’s nothing that looks particularly great until…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*69HkHtc3kUxNuOB56PuqPA.png" /><figcaption>Thanks Google</figcaption></figure><p>Right! Turns out Expo has modules for all these things and they also work in regular React Native apps thanks to Unimodules.</p><h3>Expo to the rescue</h3><p>Unimodules is a recent effort by Expo to make their native modules universal (you get the name now?). Universal means they can be used even if you are not using Expo. They can even be used with other frameworks like Flutter (but who cares 😜). Just follow<a href="https://github.com/unimodules/react-native-unimodules"> the instructions in the readme</a> to set it up for your project.</p><p>Let’s install the modules that we will need (using yarn cuz we kewl)</p><pre>yarn add expo-permissions expo-contacts expo-sms expo-mail-composer</pre><h3>Ok, show me some code</h3><p>First we need to start by fetching contacts. Here’s a hook to do this.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a8def7ed0612c1fb9791f7b4d8c46274/href">https://medium.com/media/a8def7ed0612c1fb9791f7b4d8c46274/href</a></iframe><p>This part is relatively straightforward. Permissions handling is not perfect, but I wanted to keep this code snippet sample. For production code permissions are stored as persisted global state (redux).</p><p>Next we need to transform this data into the format expected by SectionList. There’s probably a more beautiful and performant way to do this but here we go anyway:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c2bb05f0ab94877700e4733b5de9d057/href">https://medium.com/media/c2bb05f0ab94877700e4733b5de9d057/href</a></iframe><p>We’re using the useMemo hook to avoid recomputing the section data on every render. This also does a few things:</p><ul><li>Use Array.reduce to create one RowItem object for each contact’s phone number and email.</li><li>Group these objects by the first letter of the contact’s name using lodash/groupBy.</li><li>Use Object.entries to go back to an array of objects with key and data. This is what is expected by SectionList.</li></ul><p>Now that we can display the contacts we need to be able to select them. I won’t go through this code since it’s pretty simple. The last thing is we need a button that when clicked triggers the email and sms send composers:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/302a03e2194c8145b738b61e295bec36/href">https://medium.com/media/302a03e2194c8145b738b61e295bec36/href</a></iframe><p>We split our contacts in 2 groups, one for emails and one for SMS. We then show the composers one after the other if they have at least one contact of their type. We can also check the result to know if the user cancelled or actually sent a message.</p><h3>Suspicious blueberries</h3><p>You can play with the full code in <a href="https://snack.expo.io/@janic/suspicious-blueberries">this snack</a> (note that the composers do not work on the simulator, so you should open the snack in the Expo client on your physical device to try it out). The production version of this will also be available in the <a href="https://th3rdwave.app.link/j9IkRHpyLR">Th3rd Wave app</a> soon.</p><p><em>Follow our brilliantly clever (and just brilliant) guest-blogger, Janic, here on </em><a href="https://medium.com/@janicduplessis"><em>Medium</em></a><em>, on </em><a href="https://twitter.com/janicduplessis"><em>Twitter</em></a><em>, and on </em><a href="https://github.com/janicduplessis"><em>Github</em></a><em>, for more gems like these!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=7549b1b0b3af" width="1" height="1" alt=""><hr><p><a href="https://blog.expo.dev/help-your-users-share-their-love-for-your-app-with-react-native-7549b1b0b3af">Help your users share their love for your app with React Native</a> was originally published in <a href="https://blog.expo.dev">Exposition</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Under the Hood: Offline Assets in Expo]]></title>
            <link>https://blog.expo.dev/under-the-hood-offline-assets-in-expo-98614adcf9e5?source=rss-a0efb5e07003------2</link>
            <guid isPermaLink="false">https://medium.com/p/98614adcf9e5</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Janic Duplessis]]></dc:creator>
            <pubDate>Mon, 19 Mar 2018 18:05:33 GMT</pubDate>
            <atom:updated>2018-03-19T18:05:33.752Z</atom:updated>
            <content:encoded><![CDATA[<p>A lot of our Expo developers have expressed their need for better offline support, and we hear you! You can now start Expo apps offline — and here’s quick overview of how it works.</p><h3>How assets work within the Expo client</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/701/1*q-1TT2WCj0NxXGHXeNyrGg.png" /></figure><ol><li>When publishing your experience, Metro (the React Native packager) generates a map of all assets used by the app. They are then uploaded to the Expo CDN using the asset hash has its key.</li><li>Using <a href="https://github.com/expo/expo-sdk/blob/27d2b03d984a8972084857c53df1dc98a8a82699/src/Asset.js#L179">some magic in React Native</a> asset resolution, we can change the URI field to its path on the Expo CDN instead of the local file.</li></ol><pre>// React Native asset object<br>{ uri: &quot;file:///selfie.png&quot;, ... }<br>// Changes to ⬇️⬇️⬇️<br>{ uri: &quot;https://cdn.expo.io/&lt;assetHash&gt;&quot; }</pre><p>This works really well with the Expo client where we can’t include assets inside the app bundle. It also works perfectly with over-the-air (OTA) updates and has somewhat limited offline support through caching.</p><p>The main problem arises when trying to load the app from the first time when offline.</p><h3>Introducing asset bundling</h3><p>Since SDK 24, Expo supports asset bundling for better offline support with the assetBundlePatterns key in app.json .</p><p>It looks like this:</p><pre>{<br>  &quot;expo&quot;: {<br>    ...<br>    &quot;assetBundlePatterns&quot;: [<br>      &quot;assets/images/*&quot;<br>    ],<br>  }<br>}</pre><p>It uses an array of <a href="https://en.wikipedia.org/wiki/Glob_(programming)">glob patterns</a> and all assets that match it in the project will be bundled when building. We’re also working on support for ExpoKit so that should be available soon.</p><p>Here’s an overview of how it works:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/671/1*5DdiH4NQ7BA1v2veZZ7cKw.png" /><figcaption>Notice that there is no longer a direct link between the app and the Expo CDN.</figcaption></figure><ol><li>During the publish phase we can save data about all the assets we want to bundle with the app; let’s call it the “Asset Manifest”.</li><li>When building the Standalone App, we download the assets included in the Asset Manifest from the Expo CDN and copy them inside the app IPA or APK.</li><li>At runtime we know which asset are bundled with the app and can change the path to the local asset in the bundle instead of the CDN URL.</li></ol><p>It still works great with OTA updates since the asset hash will change if the asset was modified and we will download it from the network instead.</p><p>Check out the <a href="https://docs.expo.io/versions/latest/guides/offline-support.html">Expo docs for more details</a>, and make sure to update your app and the <a href="https://expo.io/tools#cli">exp CLI</a> to the latest version to try it out!</p><p>Curious for more? Follow our guest-blogger and developer-partner extraordinaire, Janic, on <a href="https://github.com/janicduplessis">Github</a> and <a href="https://twitter.com/janicduplessis">Twitter</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=98614adcf9e5" width="1" height="1" alt=""><hr><p><a href="https://blog.expo.dev/under-the-hood-offline-assets-in-expo-98614adcf9e5">Under the Hood: Offline Assets in Expo</a> was originally published in <a href="https://blog.expo.dev">Exposition</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How To Make your own *Really* Awesome Relay `<QueryRenderer />` in React Native]]></title>
            <link>https://medium.com/@janicduplessis/how-to-make-your-own-really-awesome-relay-queryrenderer-in-react-native-be9aa3c71dd3?source=rss-a0efb5e07003------2</link>
            <guid isPermaLink="false">https://medium.com/p/be9aa3c71dd3</guid>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Janic Duplessis]]></dc:creator>
            <pubDate>Wed, 24 Jan 2018 05:53:50 GMT</pubDate>
            <atom:updated>2018-01-24T05:53:50.234Z</atom:updated>
            <content:encoded><![CDATA[<p>At <a href="https://medium.com/appandflow">App &amp; Flow</a> we’ve been building apps for a little while now and have come to face some of the same hard problems in software engineering a few times. I’m of the school of thought that no abstraction is better than the wrong abstraction, so the things I will talk about come from my experience using Relay in production apps for around 2 years. This isn’t to say that I found the perfect solution to everything but I feel like this is a small step in the right direction 🙂.</p><p><em>Note that I will be using Relay in my example but something similar could very well be implemented for any other GraphQL client or even for REST APIs. It is mostly just a small wrapper around data fetching components.</em></p><h3>Problem One: Stale Data</h3><p>One thing that usually happens because of the way navigation works on mobile is that you end up with screens that never get unmounted unless the app is killed and leads to old data being displayed. On native iOS one could use viewWillAppear to refetch data but you might not want to do it that often.</p><p>So let’s start our new component that will be a wrapper around Relay’s QueryRenderer component and add some logic to refetch the data when the app comes to the foreground. As a bonus we also want to limit the refetch happen at most once per 5 minutes.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c8c8de67eab808b2bb20e2df3f1a1d44/href">https://medium.com/media/c8c8de67eab808b2bb20e2df3f1a1d44/href</a></iframe><p>Using the AppState module from React Native we can detect when the app comes to the foreground and refetch the query. Sadly there is no documented way to do that currently with Relay so we resort to using a private API.</p><h3>Problem Two: Sane Defaults for Loading States and Error Handling</h3><p>Every screen that fetches data from the network will need display some sort of loading state and handle network (and even programmer) errors in a graceful way. Instead of repeating this logic over and over we can add this to our custom QueryRenderer. If the behaviour needs to be customized we can pass a `renderLoading` and/or `renderError` prop to override the defaults.</p><p>Let’s see how that would look with the component we created in the previous step.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9eacb85635d3b6cd8ffe0c1d970adef7/href">https://medium.com/media/9eacb85635d3b6cd8ffe0c1d970adef7/href</a></iframe><p>We can leverage default props to provide the Loading and Error component and simplify the render function to only be called when data is available, the other cases can be handled by the defaults since it will almost always be the same. Killing boilerplate code one prop at a time!</p><h3>Problem Three: Make UI/UX Designers Happy</h3><p>Wouldn’t it be cool if the data faded in smoothly after finishing loading instead of having it appear suddenly. Now that we have our fancy custom QueryRenderer adding this feature to every screen that loads data should be pretty simple!</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2d982f2f1a669a0b26d1f509acccc91c/href">https://medium.com/media/2d982f2f1a669a0b26d1f509acccc91c/href</a></iframe><p>Here we use a little `setTimeout` hack to make sure we don’t fade in the data if it loaded too fast (usually from a disk or memory cache). This creates a much smoother loading experience.</p><h3>Problem Four Thousand, Six Hundred Seventy-One: … ???</h3><p>At this point our RelayUtils.js file is pretty big but it does handle a lot of things and all centralized at a single place! This make developing screens a breeze and leaves error prone code out of product code. We also get consistent handling of complex app behaviour across the app.</p><p>There are a lot more things we can build into our Renderer component but won’t cover them all here. I will share a gist at the end with some of what I have built and hopefully it can be of some use.</p><p>Here are some thoughts and things I experimented with:</p><ul><li>Handling of the Relay environment, nice to avoid having to pass it around all the time.</li><li>Memory / Disk Cache, after hacking around a bit I found a decent way to do this in Relay (disclaimer: still somewhat experimental).</li><li>Reloading data on connectivity change, very similar to the app state change one.</li><li>Offline UI, we could display a banner over the content.</li><li>Allow to render stale props, QueryRenderer shows the loading state if it receive new props it doesn’t have data for, we can customize this behaviour to make it render its old content while fetching the new one instead (This was actually implemented by forking QueryRenderer and could be interesting to upstream to Relay).</li><li>`componentDidCatch` + error reporting.</li><li>Integration with the authentication state, something like a prop that tells if the user must be authenticated to do the query. It could render a login screen in the case the user is not logged yet.</li><li>Display the iOS status bar network indicator on network requests (this is better implemented at the network layer).</li></ul><p>The main idea is to build small app specific libraries around open source libraries to provide app specific defaults and handle common cases instead of trying to handle everything in an ad-hoc kind of way.</p><h3>Show Some Code now!</h3><p>I tried to extract what was not too app specific but it might not be usable as is. Also please note that it is, at the time of the writing, based on Relay 1.5.0-rc.1.</p><p><a href="https://gist.github.com/janicduplessis/2b6f3db6b554c23dd021191b35301ab1">Final code gist</a> 👌</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=be9aa3c71dd3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native collapsible navbar]]></title>
            <link>https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a?source=rss-a0efb5e07003------2</link>
            <guid isPermaLink="false">https://medium.com/p/e51a049b560a</guid>
            <category><![CDATA[react]]></category>
            <category><![CDATA[ui]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Janic Duplessis]]></dc:creator>
            <pubDate>Mon, 06 Mar 2017 18:50:17 GMT</pubDate>
            <atom:updated>2017-03-16T17:34:17.273Z</atom:updated>
            <content:encoded><![CDATA[<p>I think a lot of you liked the article I wrote about the <a href="https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e">ScrollView animated header</a> so here is one more about another popular UI pattern that can be pretty hard to implement.</p><p>This is inspired heavily by a screen I had to make when working on the <a href="https://itunes.apple.com/ca/app/th3rd-wave/id1117576488?mt=8">Th3rdwave app</a>. Here is the final result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/296/1*mqflQw5FWIEsOa2WP33oYg.gif" /></figure><p>You can also try it on Expo with <a href="https://exp.host/@janic/collapsible-navbar">this link</a> and check out the source <a href="https://github.com/janicduplessis/collapsible-navbar">on Github</a>.</p><p>Please note that to keep the code samples brief I’ll omit some parts and mark it with `[…]`, you can check the source for the missing parts.</p><h4>Initial structure</h4><p>First let’s create the view hierarchy that we will need. Basically this is just a <em>ListView</em> with some padding equals to the navbar height and the navbar is displayed on top of it using absolute positioning. This allows translating the navbar using transforms when scrolling the <em>ListView</em>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/285876da3c95fda7e3daedac283b7128/href">https://medium.com/media/285876da3c95fda7e3daedac283b7128/href</a></iframe><h4>Adding some <em>Animated</em> values</h4><p>Next let’s create some <em>Animated</em> values. We will create 3 different <em>Animated</em> values:</p><ul><li><em>scrollAnim</em>, this is simply the current scroll y position of the <em>ListView</em></li><li><em>offsetAnim</em>, this will be used to move the navbar programmatically when needed. This will be useful when we want to fully reveal or hide the navbar at the end of a scroll gesture. We don’t want to animate the <em>scrollAnim</em> directly since as soon as scrolling happens it’s value will be reset to the scroll position and cause some weird behavior.</li><li><em>clampedScroll</em>, this is the value used to animate the navbar. We also save it in the state to avoid recreating it on each render. It is created by adding together <em>scrollAnim</em> and <em>offsetAnim</em> and then using <em>Animated.diffClamp</em>. I’ll explain what <em>diffClamp</em> does in details in the next section. It also does an interpolation on <em>scrollAnim</em> to avoid issues with the bounce effect on iOS.</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e0870fb13e07458cbaf68d889347026f/href">https://medium.com/media/e0870fb13e07458cbaf68d889347026f/href</a></iframe><h4>Animated.diffClamp</h4><p>This was added to animated recently to be able to represent the animation we want to do here. Basically we can’t simply use <em>Animated.interpolate</em> with <em>extrapolate: clamp</em> since we want the navbar to start showing up again as soon as we start scrolling back up. This is where <em>diffClamp</em> is useful, what it does is calculate the difference between the current value and the last and then clamp that value. For example for <em>Animated.diffClamp(input, 0, 10)</em> here is a table of the input and output values.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a430db0c0b75da04bf2edb0f4558dc07/href">https://medium.com/media/a430db0c0b75da04bf2edb0f4558dc07/href</a></iframe><h4>Attaching the Animated values</h4><p>Now we need to attach these values to the views and add some interpolations to map them to the opacity of the title text and the translation of the navbar view. I won’t explain these in details so if you are not familiar with <em>Animated</em> and <em>useNativeDriver</em> I suggest checking <a href="https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e">my first article</a> and <a href="http://facebook.github.io/react-native/blog/2017/02/14/using-native-driver-for-animated.html">my blog post on native animations</a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b0007bc92ee3a505b21f61dd83304573/href">https://medium.com/media/b0007bc92ee3a505b21f61dd83304573/href</a></iframe><p>At this point our example is pretty functional but one detail is missing. When you stop scrolling and the navbar is half collapsed it would be nice to animate it back to either it’s fully displayed or hidden state. This is where we will use the <em>offsetAnim</em> value we created earlier.</p><h4>The hack</h4><p>Now let’s add some code to detect when scrolling is over. Sadly I don’t know of a solution that works properly with both momentum and normal scroll. Here is the order events are called for a normal scroll:</p><ul><li>onScrollEndDrag</li></ul><p>And a momentum scroll:</p><ul><li>onScrollEndDrag</li><li>onMomentumScrollBegin</li><li>onMomentumScrollEnd</li></ul><p>What we do is start a short timer in <em>onScrollEndDrag </em>and clear it in <em>onMomentumScrollBegin</em>. In the case of a normal scroll, our method that handles animating the navbar will get called after a small delay by the timer and in the case of a momentum scroll, the timer will get cleared and our method will get called in <em>onMomentumScrollEnd</em>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bb29ad0249ab24cdda75d6d1cab34bdb/href">https://medium.com/media/bb29ad0249ab24cdda75d6d1cab34bdb/href</a></iframe><h4>The final animation</h4><p>The last thing we need is to know whether we should hide the navbar or show it. For that we need to know the value of the different <em>Animated</em> values used. To do that we can add listeners to the values and save it in an instance variable to access it in the <em>_onMomentumScrollEnd</em> method. One caveat here is that the value returned from <em>Animated.diffClamp</em> doesn’t support adding listeners to it (there is <a href="https://github.com/facebook/react-native/pull/12620">a PR</a> to support that, but it isn’t merged yet) so we have to do the same calculations that <em>diffClamp</em> does manually.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c9adc9a400bcc6a33e40e13d605c42c5/href">https://medium.com/media/c9adc9a400bcc6a33e40e13d605c42c5/href</a></iframe><p>Now that we have synchronous access to these values we can implement the logic to show or hide the navbar.</p><p>Here we want to animate the offset. If we add to its current value it will cause the navbar to hide (like if the user scrolled down) and if we subtract from it, it will cause the navbar to show. What we do is simply check the clamped scroll value and check if it has passed the half of the navbar. We also want to make sure we don’t hide the navbar if we haven’t scrolled enough yet or it will show a blank space where the navbar is supposed to be.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b687d38601b30ed2b377e828c7eecbc9/href">https://medium.com/media/b687d38601b30ed2b377e828c7eecbc9/href</a></iframe><h4>Well that was something…</h4><p>Implementing these advanced UI behaviors is definitely tricky, either natively or with React Native (with the exception of Android that has developed <a href="https://developer.android.com/reference/android/support/design/widget/AppBarLayout.html">a nice library</a> to deal with this). Ideally we’d have some abstractions to avoid having to implementing this when developing an app.</p><p>See the final code <a href="https://github.com/janicduplessis/collapsible-navbar">here</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e51a049b560a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/appandflow/react-native-collapsible-navbar-e51a049b560a">React Native collapsible navbar</a> was originally published in <a href="https://medium.com/appandflow">App &amp; Flow</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[React Native ScrollView animated header]]></title>
            <link>https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e?source=rss-a0efb5e07003------2</link>
            <guid isPermaLink="false">https://medium.com/p/10a18cb9469e</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[react-native]]></category>
            <dc:creator><![CDATA[Janic Duplessis]]></dc:creator>
            <pubDate>Wed, 04 May 2016 02:04:17 GMT</pubDate>
            <atom:updated>2018-03-16T04:39:59.233Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Update: Changed the project source to use Expo and changed the structure a little bit to use transforms instead of height to be able to leverage the native driver. See </em><a href="https://github.com/janicduplessis/react-native-scrollable-header/commit/f8eed76834a77887564077f49078cef7342616db"><em>this commit</em></a><em> for the changes I made.</em></p><p>Here’s a walkthrough that shows how to build a header that is animated with the scroll position of a <em>ScrollView</em>. I had to build something similar for an app and found out that it was really easy to do in React Native using the <em>Animated</em> API.</p><p>Here’s the final result:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/260/1*PxoEr2Z45HyyFlZHHqbq8w.gif" /><figcaption>Final result</figcaption></figure><p>You can also try it on Expo <a href="https://exp.host/@janic/scrollable-header">here</a> and find the final source on <a href="https://github.com/janicduplessis/react-native-scrollable-header/blob/master/App.js">github</a>.</p><h3>How does it work?</h3><p>The idea is to render a header <strong>over</strong> the <em>ScrollView</em> using <em>position: ‘absolute’ </em>and adding a margin to the top of the <em>ScrollView</em> to offset for the header. Then we can simply animate the header height using the <em>ScrollView</em> scroll position.</p><h3>Time for some code!</h3><p>Let’s start by creating a <em>ScrollView</em> with some content and import everything we will need for the next steps.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/aef4ff1215ffadbe7452c692a86ec901/href">https://medium.com/media/aef4ff1215ffadbe7452c692a86ec901/href</a></iframe><p>Next we need to create the header <em>View</em> and add a margin to the <em>ScrollView</em> content so it’s content is not under the header. We will also add a title for the header. Let’s add the following <em>View</em> after the <em>ScrollView</em>. We will use an <em>Animated.View</em> since it is going to be animated eventually.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fd0d265e955b16f4313be7460687521f/href">https://medium.com/media/fd0d265e955b16f4313be7460687521f/href</a></iframe><p>And the following styles:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9c8d5b06c9f45534506801cd4faf1ade/href">https://medium.com/media/9c8d5b06c9f45534506801cd4faf1ade/href</a></iframe><p>We will also define some constants for the header sizes that will be used to interpolate the scroll position value.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/99994935de0daa12bdea3e0655e9c947/href">https://medium.com/media/99994935de0daa12bdea3e0655e9c947/href</a></iframe><p>Now we should have the desired initial layout and everything ready for the next step.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*1Y7O0Rk-wbZL02owxXM-_Q.png" /><figcaption>That’s a start…</figcaption></figure><h3>Animation time</h3><p>React Native has a very powerful declarative animation API that allows to animate a value but also bind it’s value to an event. In this case we will want to bind an animated value to the <em>ScrollView</em> Y scroll position. To do so, we use an <em>Animated.event</em> with the <em>ScrollView</em> <em>onScroll</em> prop.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9892bf67a4cbbb559c50035a478e5a3e/href">https://medium.com/media/9892bf67a4cbbb559c50035a478e5a3e/href</a></iframe><p>First we create an <em>Animated.Value</em> which is a simple value that can be animated using the <em>Animated</em> API.</p><p>Then we bind the animated value to the <em>ScrollView</em> scroll position. To do that we use an <em>Animated.event</em> with a mapping to the event object property that we want to bind to the animated value. In this case it is <em>&lt;eventObject&gt;.nativeEvent.contentOffset.y</em>.</p><p>Then we use the interpolate method of the <em>Animated.Value</em> to map the scroll position to the desired header height. What we want is that when the scroll position is at 0, the header is at <em>HEADER_MAX_HEIGHT</em> and when the scroll position has moved to the difference between <em>HEADER_MAX_HEIGHT</em> and <em>HEADER_MIN_HEIGHT</em>, the header is at <em>HEADER_MIN_HEIGHT</em>.</p><p>Finally we just set the height to the animated value for the header view. We also set the <em>scrollEventThrottle</em> prop to 16 so events are sent often enough to have a smooth animation.</p><p>At this point the header will move with the scroll position and using the same technique we can animate pretty much anything we want in the header while the user is scrolling. Time to use your creativity :)</p><h3>More animations</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/548/1*9OBTDUSE0XEgZLemVBFfxg.png" /></figure><p>Now this header is a bit boring let’s add our favorite cat picture with a parallax effect to make it nicer. To do this let’s add two new interpolated animated values and an <em>Image </em>component.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/498ead9d3ca14df727026f57e0b76f4e/href">https://medium.com/media/498ead9d3ca14df727026f57e0b76f4e/href</a></iframe><p>We use the same initial interpolation values but here we want to output an opacity value and a translation of the image to create the parallax effect. We also animate the opacity a bit differently by adding a 3rd breakpoint in the middle. This way the opacity will only start to decay when the header has scrolled halfway.</p><p>Now all that is missing is scaling / translating the header title a bit but I’ll leave that as an exercise.</p><h3>Wrapping up</h3><p>The <em>Animated</em> API makes it very easy to create very complex animations by interpolating a value in multiple different ways. It also allows to keep the state super simple. Everything is based on one value, all the animation complexity is handled in the render function with the interpolations.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10a18cb9469e" width="1" height="1" alt=""><hr><p><a href="https://medium.com/appandflow/react-native-scrollview-animated-header-10a18cb9469e">React Native ScrollView animated header</a> was originally published in <a href="https://medium.com/appandflow">App &amp; Flow</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>