<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Zia Ul Rehman</title>
    <description>The latest articles on DEV Community by Zia Ul Rehman (@ziaulrehman40).</description>
    <link>https://dev.to/ziaulrehman40</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F200095%2F3a5231af-efa6-43fd-90c9-4fe72fc97680.jpg</url>
      <title>DEV Community: Zia Ul Rehman</title>
      <link>https://dev.to/ziaulrehman40</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ziaulrehman40"/>
    <language>en</language>
    <item>
      <title>Story of an SQL query ~10x speed-up with a simple tweak</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Thu, 30 Nov 2023 19:15:08 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/story-of-an-sql-query-10x-speed-up-with-a-simple-a-tweak-3odh</link>
      <guid>https://dev.to/ziaulrehman40/story-of-an-sql-query-10x-speed-up-with-a-simple-a-tweak-3odh</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9415oq3opodhmwlaz5rd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9415oq3opodhmwlaz5rd.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s re-visit basics of debugging performance issues and importance of monitoring tools(focusing on web apps), with a story of simple tweak improving a bottleneck query performance up-to 10x&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;A walk-through of simple debugging process for performance issues, and how simple tweaks can make your code run so much faster with minimal efforts to spare on performance. (&lt;a href="https://en.wikipedia.org/wiki/Pareto_principle#In_computing" rel="noopener noreferrer"&gt;80/20 rule&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start fixing bad performance
&lt;/h2&gt;

&lt;p&gt;So you have bad performing app or pages, now what?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There are two obvious paths&lt;/strong&gt; , as you being the creator or maintainer of the app, you already have basic idea of where the bottle necks are, which pages load slow etc, so you can just go and try fixing things, This may work for smaller apps, but of-course, this is not a systematic way.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;good approach&lt;/strong&gt; for most production apps, is to have things setup in a &lt;strong&gt;systematic manner&lt;/strong&gt; , have performance and server monitoring setup done right(don’t over kill), collect data, analyze data and quickly fix the issues with the richness of insights you get from the right tooling.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I suggest almost always going with the systematic manner, no matter the size of the app. If you have long term plans with you app that is.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance monitoring tooling
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I am assuming you have an app already in production of test phase, which is facing memory bloat issues or slow API response times, where do you start with?&lt;/strong&gt; Of-course, you need &lt;strong&gt;tooling&lt;/strong&gt; that can help you determine not only the slowness causing areas in your system, but also the breakdown of insights like how much time DB is taking, how much time each function is taking and so on.&lt;/p&gt;

&lt;p&gt;We have &lt;strong&gt;many tools&lt;/strong&gt; for this, my personal favourites, new relic APM, and &lt;a href="https://www.skylight.io" rel="noopener noreferrer"&gt;Skylight&lt;/a&gt;(it focuses on Ruby on Rails apps, so has more niche offerings)(not affiliated to any in any way). I use both regularly. Of-course &lt;strong&gt;you can use any APM&lt;/strong&gt; , and even 1 single tool is enough, what you need at the end is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ability to see slow end points&lt;/strong&gt; or processes based on a few parameters:

&lt;ol&gt;
&lt;li&gt;_ &lt;strong&gt;Slowest average response time&lt;/strong&gt; _ (transactions which perform the worst)&lt;/li&gt;
&lt;li&gt;_ &lt;strong&gt;Most time consuming&lt;/strong&gt; _ (transactions which collectively takes most of the server time)&lt;/li&gt;
&lt;li&gt;_ &lt;strong&gt;Throughput&lt;/strong&gt; _ (most popular transactions)&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Ability to provide insights&lt;/strong&gt; into each of the server transaction(we define a transaction as being a unit of work, be it an API call or a background job)

&lt;ol&gt;
&lt;li&gt;_ &lt;strong&gt;Time being taken&lt;/strong&gt; _ by each section of the code&lt;/li&gt;
&lt;li&gt;Total time spent on _ &lt;strong&gt;DB vs in app&lt;/strong&gt; _ processing&lt;/li&gt;
&lt;li&gt;Break down of all the &lt;strong&gt;&lt;em&gt;SQL queries&lt;/em&gt;,&lt;/strong&gt; and their run time in the transaction&lt;/li&gt;
&lt;li&gt;Aggregation of these insights, like overall slowest queries in the system etc&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;In addition, some tools provide &lt;strong&gt;even granular insights&lt;/strong&gt; by trying to identify N+1 queries, repeated queries patterns and showing exactly how much time is spent on each query (i find skylight very good at this)&lt;/li&gt;

&lt;li&gt;Also, another really helpful thing, especially when debugging memory issues, is &lt;strong&gt;tooling which can tell us how much memory allocations are done in each section of the code&lt;/strong&gt; , being run in a single transaction, and of-course, total average allocations per transaction. (again, i find skylight better in this regard)&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;If we have our tooling setup right, and we are getting as much insights as we need, tool name at the end doesn’t matter as such. It’s very much critical to have your setup right! Everything else, is on your taste and maybe somewhat dependent on your framework.&lt;br&gt;&lt;br&gt;
Just one thing is to ensure &lt;strong&gt;working in the right direction, i.e focusing on high impact or frequent transactions instead of just trying to fix the slowest one&lt;/strong&gt; , that might not even be called in days.&lt;/p&gt;
&lt;h2&gt;
  
  
  After Identifying Slowest Transactions
&lt;/h2&gt;

&lt;p&gt;Now after all the basic setup, you have your slowest transactions at hand, you pick single transaction you want to focus on, based the impact of that transaction(like an endpoint directly impacting core function of the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With right tooling, you should have the all the data you need&lt;/strong&gt; (even code section level insights) to help you find bottle-necks in slow transactions.&lt;br&gt;&lt;br&gt;
We don’t intend going to a lot of details of each type of fixes you can make with these insights, any engineer can use this data and build upon this information their hypothesis, verify them, and than fix the issues they have identified.&lt;/p&gt;

&lt;p&gt;Only one thing i would like to highlight is the importance of benchmarking.&lt;/p&gt;
&lt;h2&gt;
  
  
  Benchmarking your fixes
&lt;/h2&gt;

&lt;p&gt;Benchmarking in this context is having a base performance stats logged, and than monitoring the performance of same transactions after you fix them, to see how they actually perform and stand against the previously logged transaction performance&lt;/p&gt;
&lt;h2&gt;
  
  
  Simple SQL query tweak improved it ~10x
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(We will mask some details as to ensure code privacy)&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Also, &lt;em&gt;we will see why i mentioned 80/20 rules on top, and how it helps us move fast and focus on what matters most and create most impact with minimal efforts.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We had an endpoint in our app, which was fetching Posts i am associated with in some manners, say we have &lt;em&gt;post&lt;/em&gt; table and associated tables like &lt;em&gt;post_statuses&lt;/em&gt;, &lt;em&gt;post_reports&lt;/em&gt;, &lt;em&gt;post_events&lt;/em&gt;, &lt;em&gt;post_collaborators&lt;/em&gt; and so on. &lt;strong&gt;This endpoints fetches all posts where&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Post is in specific statuses (published, draft etc)&lt;/li&gt;
&lt;li&gt;Post has any pending reports assigned to current user for review (abuse reports)&lt;/li&gt;
&lt;li&gt;Current user is collaborator in the post&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plus, all of these posts should be ordered in a &lt;em&gt;very specific order&lt;/em&gt;, with some data on top for each report, i.e:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Order by latest activity (activity is defined across multiple tables’ operations)&lt;/li&gt;
&lt;li&gt;Include unread comments count (to show kind of like whatsapp shows unread message count)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now this defines the baseline of the structure and requirements, of-course there are more details to the matter, and the long single SQL query, taking like 25-30 seconds per request.&lt;/p&gt;
&lt;h2&gt;
  
  
  Original Query
&lt;/h2&gt;

&lt;p&gt;Looked something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT posts.*, COUNT(prs.id) AS pending_reports, posts.*, 
GREATEST(MAX(post_comments.created_at), MAX(post_events.created_at), GREATEST(posts.created_at, posts.updated_at)) AS latest_activity,
count(DISTINCT(user_comments.id)) FILTER (where user_comments.id IS NOT NULL) AS unread_count,
GREATEST(MAX(comments.created_at), posts.created_at) AS last_activity 

FROM "posts"
INNER JOIN "post_statuses" ON "post_statuses"."post_id" = "posts"."id" 
LEFT JOIN post_reports as pr ON pr.reportable_id = posts.id AND pr.reportable_type='Post' 
LEFT JOIN post_report_statuses as prs ON prs.post_report_id = pr.id AND pr.active = true AND pr.user_id = 1 
LEFT JOIN comments AS post_comments ON post_comments.commentable_id = posts.id AND post_comments.commentable_type = 'post' 
LEFT JOIN post_events ON post_events.post_id = posts.id 
LEFT JOIN comments ON comments.commentable_id = posts.id AND comments.commentable_type = 'post' 
LEFT JOIN user_comments ON user_comments.comment_id = comments.id AND user_comments.user_id = 1 AND user_comments.read_at IS NULL 
LEFT JOIN post_collaborators as pco ON pco.post_id = posts.id AND pco.user_id = 1 AND pco.post_signed_off = 'f' 
WHERE "posts"."id" IN (
     SELECT DISTINCT "posts"."id" 
     FROM "posts" 
     LEFT JOIN post_collaborators AS current_post_collaborators ON current_post_collaborators.post_id = posts.id AND current_post_collaborators.deleted_at IS NULL 
     LEFT JOIN post_reports AS user_reports ON posts.id = user_reports.reportable_id AND user_reports.reportable_type = 'post' 
     LEFT JOIN post_report_statuses ON user_reports.id = post_report_statuses.post_report_id AND post_report_statuses.status = ANY(ARRAY [0, 1, 2]) 
     WHERE (posts.moderator_id = 1 OR current_post_collaborators.user_id = 1 OR user_reports.user_id = 1 )
) AND (
"posts"."creator_id" IN (SELECT "users"."id" FROM "users" WHERE ((users.first_name || ' ' || COALESCE(users.last_name, '')) ILIKE '%%')) 
OR "posts"."moderator_id" IN (SELECT "users"."id" FROM "users" WHERE ((users.first_name || ' ' || COALESCE(users.last_name, '')) ILIKE '%%'))) 
GROUP BY "posts"."id" 
ORDER BY latest_activity DESC, last_activity DESC 
LIMIT 50 OFFSET 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, &lt;strong&gt;there are some obvious problems in this SQL&lt;/strong&gt; , remember this SQL is produced by an ORM, not directly written by a developer, and ORMs while make things easy and fast to build, sometimes can introduce un-intended complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Original Query Plan
&lt;/h2&gt;

&lt;p&gt;Here is how its &lt;a href="https://pganalyze.com/docs/explain/basics-of-postgres-query-planning" rel="noopener noreferrer"&gt;Query Plan&lt;/a&gt; with Explain Analyze looked like this(don’t try to read it in full)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Limit (cost=237736.02..237736.15 rows=50 width=694) (actual time=27772.897..27772.922 rows=50 loops=1)
   -&amp;gt; Sort (cost=237736.02..237785.96 rows=19974 width=694) (actual time=27772.895..27772.916 rows=50 loops=1)
         Sort Key: (GREATEST(max(post_comments.created_at), max(post_events.created_at), GREATEST(posts.created_at, posts.updated_at))) DESC, (GREATEST(max(comments.created_at), posts.created_at)) DESC
         Sort Method: top-N heapsort Memory: 121kB
         -&amp;gt; GroupAggregate (cost=33452.05..237072.50 rows=19974 width=694) (actual time=373.867..27771.558 rows=685 loops=1)
               Group Key: posts.id
               -&amp;gt; Merge Left Join (cost=33452.05..226439.26 rows=685580 width=371) (actual time=369.564..27562.155 rows=1371572 loops=1)
                     Merge Cond: (posts.id = pco.post_id)
                     -&amp;gt; Merge Join (cost=32483.89..223659.06 rows=685580 width=371) (actual time=368.498..27302.152 rows=1371572 loops=1)
                           Merge Cond: (post_statuses.post_id = posts_1.id)
                           -&amp;gt; Merge Left Join (cost=27619.84..193204.04 rows=7477059 width=379) (actual time=104.873..22591.305 rows=71784103 loops=1)
                                 Merge Cond: (posts.id = post_events.post_id)
                                 -&amp;gt; Merge Left Join (cost=27619.55..70066.69 rows=1138329 width=371) (actual time=104.821..1694.553 rows=4350298 loops=1)
                                       Merge Cond: (posts.id = post_comments.commentable_id)
                                       -&amp;gt; Merge Left Join (cost=27619.25..37371.24 rows=262806 width=363) (actual time=104.790..336.002 rows=379658 loops=1)
                                             Merge Cond: (posts.id = comments.commentable_id)
                                             -&amp;gt; Merge Join (cost=2181.16..7665.76 rows=60674 width=347) (actual time=4.289..89.766 rows=79948 loops=1)
                                                   Merge Cond: (posts.id = post_statuses.post_id)
                                                   -&amp;gt; Merge Left Join (cost=2180.74..4579.45 rows=19974 width=339) (actual time=4.271..44.332 rows=26882 loops=1)
                                                         Merge Cond: (posts.id = pr.reportable_id)
                                                         -&amp;gt; Index Scan using posts_pkey on posts (cost=2096.29..4435.40 rows=19974 width=331) (actual time=3.439..35.952 rows=26630 loops=1)
                                                               Filter: ((hashed SubPlan 1) OR (hashed SubPlan 2))
                                                               SubPlan 1
                                                                 -&amp;gt; Seq Scan on users (cost=0.00..1043.38 rows=1850 width=8) (actual time=0.020..3.018 rows=1850 loops=1)
                                                                       Filter: ((((first_name)::text || ' '::text) || (COALESCE(last_name, ''::character varying))::text) ~~* '%%'::text)
                                                               SubPlan 2
                                                                 -&amp;gt; Seq Scan on users users_1 (cost=0.00..1043.38 rows=1850 width=8) (never executed)
                                                                       Filter: ((((first_name)::text || ' '::text) || (COALESCE(last_name, ''::character varying))::text) ~~* '%%'::text)
                                                         -&amp;gt; Sort (cost=84.45..86.39 rows=773 width=12) (actual time=0.826..1.038 rows=777 loops=1)
                                                               Sort Key: pr.reportable_id
                                                               Sort Method: quicksort Memory: 61kB
                                                               -&amp;gt; Hash Right Join (cost=29.62..47.37 rows=773 width=12) (actual time=0.463..0.673 rows=777 loops=1)
                                                                     Hash Cond: (prs.post_report_id = pr.id)
                                                                     Join Filter: (pr.active AND (pr.user_id = 1))
                                                                     Rows Removed by Join Filter: 775
                                                                     -&amp;gt; Seq Scan on post_report_statuses tas (cost=0.00..15.72 rows=772 width=16) (actual time=0.005..0.078 rows=779 loops=1)
                                                                     -&amp;gt; Hash (cost=19.96..19.96 rows=773 width=21) (actual time=0.321..0.322 rows=777 loops=1)
                                                                           Buckets: 1024 Batches: 1 Memory Usage: 57kB
                                                                           -&amp;gt; Seq Scan on post_reports ta (cost=0.00..19.96 rows=773 width=21) (actual time=0.014..0.204 rows=777 loops=1)
                                                                                 Filter: ((reportable_type)::text = 'post'::text)
                                                                                 Rows Removed by Filter: 24
                                                   -&amp;gt; Index Only Scan using index_post_statuses_on_post_id on post_statuses (cost=0.29..2265.14 rows=80899 width=8) (actual time=0.015..27.638 rows=79948 loops=1)
                                                         Heap Fetches: 28565
                                             -&amp;gt; Materialize (cost=25438.10..26014.87 rows=115355 width=24) (actual time=100.494..154.413 rows=370263 loops=1)
                                                   -&amp;gt; Sort (cost=25438.10..25726.48 rows=115355 width=24) (actual time=100.490..120.023 rows=115303 loops=1)
                                                         Sort Key: comments.commentable_id
                                                         Sort Method: external merge Disk: 2952kB
                                                         -&amp;gt; Hash Left Join (cost=4747.85..13373.21 rows=115355 width=24) (actual time=4.067..61.603 rows=115304 loops=1)
                                                               Hash Cond: (comments.id = user_comments.comment_id)
                                                               -&amp;gt; Seq Scan on comments (cost=0.00..8322.54 rows=115355 width=24) (actual time=0.006..39.808 rows=115304 loops=1)
                                                                     Filter: ((commentable_type)::text = 'post'::text)
                                                                     Rows Removed by Filter: 27899
                                                               -&amp;gt; Hash (cost=4734.53..4734.53 rows=1066 width=16) (actual time=4.054..4.055 rows=807 loops=1)
                                                                     Buckets: 2048 Batches: 1 Memory Usage: 54kB
                                                                     -&amp;gt; Bitmap Heap Scan on user_comments (cost=39.41..4734.53 rows=1066 width=16) (actual time=0.567..3.899 rows=807 loops=1)
                                                                           Recheck Cond: (user_id = 1)
                                                                           Filter: (read_at IS NULL)
                                                                           Rows Removed by Filter: 2899
                                                                           Heap Blocks: exact=1935
                                                                           -&amp;gt; Bitmap Index Scan on index_user_comments_on_user_id (cost=0.00..39.15 rows=3030 width=0) (actual time=0.270..0.270 rows=3889 loops=1)
                                                                                 Index Cond: (user_id = 1)
                                       -&amp;gt; Materialize (cost=0.29..17628.74 rows=115355 width=16) (actual time=0.027..391.368 rows=4340903 loops=1)
                                             -&amp;gt; Index Scan using index_comments_on_commentable_id on comments post_comments (cost=0.29..17340.35 rows=115355 width=16) (actual time=0.024..117.440 rows=115303 loops=1)
                                                   Filter: ((commentable_type)::text = 'post'::text)
                                                   Rows Removed by Filter: 9612
                                 -&amp;gt; Materialize (cost=0.29..8572.97 rows=174931 width=16) (actual time=0.049..4195.281 rows=71784103 loops=1)
                                       -&amp;gt; Index Scan using index_post_events_on_post_id on post_events (cost=0.29..8135.64 rows=174931 width=16) (actual time=0.047..97.077 rows=173202 loops=1)
                           -&amp;gt; Unique (cost=4864.04..4876.19 rows=2430 width=8) (actual time=53.422..53.979 rows=685 loops=1)
                                 -&amp;gt; Sort (cost=4864.04..4870.12 rows=2430 width=8) (actual time=53.419..53.623 rows=904 loops=1)
                                       Sort Key: posts_1.id
                                       Sort Method: quicksort Memory: 25kB
                                       -&amp;gt; Hash Left Join (cost=1873.16..4727.40 rows=2430 width=8) (actual time=11.725..53.306 rows=904 loops=1)
                                             Hash Cond: (posts_1.id = user_reports.reportable_id)
                                             Filter: ((posts_1.moderator_id = 1) OR (current_post_collaborators.user_id = 1) OR (user_reports.user_id = 1))
                                             Rows Removed by Filter: 70531
                                             -&amp;gt; Hash Right Join (cost=1813.22..3964.41 rows=66906 width=24) (actual time=10.900..43.152 rows=70403 loops=1)
                                                   Hash Cond: (current_post_collaborators.post_id = posts_1.id)
                                                   -&amp;gt; Seq Scan on post_collaborators current_post_collaborators (cost=0.00..1975.53 rows=66906 width=16) (actual time=0.016..12.351 rows=66718 loops=1)
                                                         Filter: (deleted_at IS NULL)
                                                         Rows Removed by Filter: 23609
                                                   -&amp;gt; Hash (cost=1480.32..1480.32 rows=26632 width=16) (actual time=10.868..10.869 rows=26632 loops=1)
                                                         Buckets: 32768 Batches: 1 Memory Usage: 1505kB
                                                         -&amp;gt; Seq Scan on posts posts_1 (cost=0.00..1480.32 rows=26632 width=16) (actual time=0.009..6.641 rows=26632 loops=1)
                                             -&amp;gt; Hash (cost=50.28..50.28 rows=773 width=12) (actual time=0.733..0.735 rows=779 loops=1)
                                                   Buckets: 1024 Batches: 1 Memory Usage: 45kB
                                                   -&amp;gt; Hash Right Join (cost=29.62..50.28 rows=773 width=12) (actual time=0.306..0.625 rows=779 loops=1)
                                                         Hash Cond: (post_report_statuses.post_report_id = user_reports.id)
                                                         -&amp;gt; Seq Scan on post_report_statuses (cost=0.00..18.62 rows=772 width=8) (actual time=0.009..0.136 rows=779 loops=1)
                                                               Filter: (status = ANY ('{0,1,2}'::integer[]))
                                                         -&amp;gt; Hash (cost=19.96..19.96 rows=773 width=20) (actual time=0.289..0.290 rows=777 loops=1)
                                                               Buckets: 1024 Batches: 1 Memory Usage: 51kB
                                                               -&amp;gt; Seq Scan on post_reports user_reports (cost=0.00..19.96 rows=773 width=20) (actual time=0.009..0.181 rows=777 loops=1)
                                                                     Filter: ((reportable_type)::text = 'post'::text)
                                                                     Rows Removed by Filter: 24
                     -&amp;gt; Sort (cost=968.16..968.80 rows=254 width=8) (actual time=1.063..15.024 rows=245069 loops=1)
                           Sort Key: pco.post_id
                           Sort Method: quicksort Memory: 25kB
                           -&amp;gt; Bitmap Heap Scan on post_collaborators tus (cost=358.00..958.02 rows=254 width=8) (actual time=0.871..1.038 rows=138 loops=1)
                                 Recheck Cond: (user_id = 1)
                                 Filter: (NOT post_signed_off)
                                 Heap Blocks: exact=113
                                 -&amp;gt; BitmapAnd (cost=358.00..358.00 rows=254 width=0) (actual time=0.854..0.855 rows=0 loops=1)
                                       -&amp;gt; Bitmap Index Scan on index_post_collaborators_on_user_id (cost=0.00..10.06 rows=769 width=0) (actual time=0.055..0.055 rows=741 loops=1)
                                             Index Cond: (user_id = 1)
                                       -&amp;gt; Bitmap Index Scan on index_post_collaborators_on_post_signed_off (cost=0.00..347.56 rows=29769 width=0) (actual time=0.779..0.779 rows=30347 loops=1)
                                             Index Cond: (post_signed_off = false)
 Planning Time: 5.432 ms
 Execution Time: 27773.891 ms
(108 rows)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is obviously too long to read, but there are some key areas like :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Actual cost and planned cost to look closely into, especially where it is highest. &lt;/li&gt;
&lt;li&gt;Lookout for the sequential scans, and fix them with appropriate indexes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Start with small and obvious things
&lt;/h2&gt;

&lt;p&gt;But remember &lt;em&gt;we are here following 80/20 rule&lt;/em&gt;, we went systematically to find bottlenecks, but we will not go in and fix everything we see not making sense, we will start small, and see impact while we improve. First lets remove the useless blocks at the end which look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(
"posts"."creator_id" IN (SELECT "users"."id" FROM "users" WHERE ((users.first_name || ' ' || COALESCE(users.last_name, '')) ILIKE '%%')) 
OR "posts"."moderator_id" IN (SELECT "users"."id" FROM "users" WHERE ((users.first_name || ' ' || COALESCE(users.last_name, '')) ILIKE '%%'))) 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;These likes are literally doing nothing&lt;/em&gt;, they will validate to true for all ALL users, so we don’t really need them, they are intended to be there when there is a free form search on username. But we had a missing check which was causing this SQL to be written even when not needed.&lt;/p&gt;

&lt;p&gt;Removing this made a huge impact, like a 500ms impact on a 30s query, lol. But that was a start. Don’t worry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Remove duplicate join
&lt;/h2&gt;

&lt;p&gt;At this stage, we noticed a duplicate join:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LEFT JOIN comments AS post_comments ON post_comments.commentable_id = posts.id AND post_comments.commentable_type = 'post' 
LEFT JOIN comments ON comments.commentable_id = posts.id AND comments.commentable_type = 'post' 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;These are essentially joining on same conditions&lt;/strong&gt; , but one is aliased and one is not. Was not something looked like could make a huge difference, but hey, continue with small fixes.&lt;/p&gt;

&lt;p&gt;So i removed one join and re-tried the final query… and we got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Planning Time: 9.905 ms
 Execution Time: 2151.695 ms
(93 rows)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is &lt;strong&gt;2.1 seconds, vs the original 25-30 seconds&lt;/strong&gt; variant. Voila, 10-15x improvements with 1 simple tweak. This is where we decided that it is good enough to push in first iteration of the enhancements, we opened up our rails models, and fixed a couple of scopes, dried out some SQL in different scopes, and we had this all sorted.&lt;/p&gt;

&lt;p&gt;(&lt;em&gt;As we pushed this fix, we continued to monitor the performance benchmarks for respective transactions, and we saw up-to 10x improvements in overall stats of slowest response time, and average response time etc.&lt;/em&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Further optimisations, learning where to stop
&lt;/h2&gt;

&lt;p&gt;At this stage, we have achieved our 80% from the 80/20 rule, we have acceptable performance for this endpoint, i know &lt;strong&gt;2 seconds is still too slow&lt;/strong&gt; , but this was for worst case, average case is gone under 400ms, which is still not great, but acceptable in our use case(We have other slow endpoints to take care of).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But, what i would like to touch base on, is why 80/20 rule says that 80% of the desired outcomes can be achieved with 20% efforts(in the right direction), and rest 20% of the desired outcomes, take 80% of your time, with showing you how much efforts more optimizations would require.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you look at the query again, there is a complex select:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GREATEST(MAX(post_comments.created_at), MAX(post_events.created_at), GREATEST(posts.created_at, posts.updated_at)) AS latest_activity
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what’s more is, &lt;strong&gt;we are even sorting on this!&lt;/strong&gt; Which means, for all eligible rows, our DB has to calculate this giant value and than sort on the result of select. Which is obviously going to be very slow.&lt;br&gt;&lt;br&gt;
One solution which comes to mind is, that we can &lt;strong&gt;store a calculated latest_activity_timestamp value for posts&lt;/strong&gt; , and maintain that value in a reasonable manner(via model callbacks or DB triggers), and have it indexed so we can run our queries in the most optimal manner.&lt;/p&gt;

&lt;p&gt;BUT, as you can tell, introducing a new calculated value column in DB, and making sure all triggers are set right against this, in going to be a significant effort, compared to what we did before by just removing 2-3 lines of SQL and achieving 10x improving.&lt;/p&gt;

&lt;p&gt;I know doing this complex activity, may give us more 10x improvement(bring 2s query time to 200ms), but &lt;strong&gt;that is not worth it right now for us, we will do this when it bites us more than other high impact things&lt;/strong&gt;. We have got 80% of the desired results and we saved plenty of time to spend on other problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So lesson here is to learn where to start, where to stop and finding the sweat spot for your use case.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>optimizations</category>
      <category>sql</category>
      <category>uncategorized</category>
      <category>degging</category>
    </item>
    <item>
      <title>Working around un-maintained redux-token-auth for redux and react 17 upgrade</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Sun, 26 Dec 2021 12:56:45 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/working-around-un-maintained-redux-token-auth-for-redux-and-react-17-upgrade-4m9k</link>
      <guid>https://dev.to/ziaulrehman40/working-around-un-maintained-redux-token-auth-for-redux-and-react-17-upgrade-4m9k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xgIV5sFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ziatechblog.files.wordpress.com/2021/12/image.png%3Fw%3D1024" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xgIV5sFt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://ziatechblog.files.wordpress.com/2021/12/image.png%3Fw%3D1024" alt="" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A small case study from our recent efforts to upgrade our legacy react app, which included upgrading and replacing a number of libraries. This one focuses on one of them, that is redux-token-auth.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tl;DR:&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/kylecorbelli/redux-token-auth"&gt;redux-token-auth&lt;/a&gt; had been a go to choice for many rails devs who started working with standalone frontends in react. But it has been majorly un-maintained, while on the journey of upgrading our front-end stack, this was becoming a blocker as &lt;strong&gt;it was locking to older version of redux and react etc, also had security issues. We ended up with a custom clone where we managed to fix as much as we needed&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why are we sharing this?
&lt;/h2&gt;

&lt;p&gt;This is a &lt;em&gt;small case study&lt;/em&gt; of how we upgraded this package, and what issues we faced, what route we took and where we landed, we share with you what issues faced AFTER the upgrade. And how we resolved them. The purpose of sharing this small story is that before we attempted this upgrade, we tried searching on the internet for this, and didn’t found much, so we had to do our own R&amp;amp;D and &lt;strong&gt;it took us ~2-3 days of effort&lt;/strong&gt; to finalize this upgrade. So if anyone is upgrading their stack(which we should do often) this should save them some time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full Story
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Intro&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kylecorbelli/redux-token-auth"&gt;redux-token-auth&lt;/a&gt; is a great library. What it mainly does is it provides a plug and play auth implementation functionality for ruby on rails based APIs which implement popular &lt;a href="https://github.com/lynndylanhurley/devise_token_auth"&gt;devise_token_auth&lt;/a&gt; for auth handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Problem is, &lt;strong&gt;redux-token-auth is un-maintained for a long time&lt;/strong&gt; , says so on readme, has number of open issues, with little to no activity. I don’t work in typescript, otherwise I would LOVE to take it over. &lt;strong&gt;(PS: A great opportunity if you want to actively work on an open source project, which is already used a lot, this is looking for maintainers.)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It works fine, for the most part, only when we tried upgrading redux and react, there were strange issues, which when we investigated when upgrading, pointed to multiple major versions of redux and react being in our package-lock, and being used by different components. Which is obviously a problem. There were already open issues for this, with no solution, so we had to dig.&lt;br&gt;&lt;br&gt;
Also there was old version of &lt;strong&gt;axios with vulnerabilities&lt;/strong&gt; , which we had to get rid of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We tried with &lt;strong&gt;exploring the existing clones&lt;/strong&gt; , there were number of clones, as its been years for this package being un-maintained. We looked into many of the clones, mainly looking into change-logs and commit changes from core. Many clones were also fixing 1 or two issues. It seemed like we will have to learn typescript and than do the fixes, which i was not willing as much to do.&lt;/p&gt;

&lt;p&gt;After hours of search and try, &lt;strong&gt;we found a relatively &lt;a href="https://github.com/kazukinagata/redux-token-auth/tree/develop"&gt;healthy clon&lt;/a&gt;&lt;/strong&gt;&lt;a href="https://github.com/kazukinagata/redux-token-auth/tree/develop"&gt;e&lt;/a&gt; with a LOT of changes from original one, it felt a bit dangerous but we went through the changes_(I don’t do typescript, but i can understand its code, like someone who doesn’t speak a language but can understand it somewhat when spoken to him )_. (it had all changes in develop and not in master/main, which did caused some confusion and overlooking in start.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This clone had already done the major parts of our work react and redux dependencies were pinned to latest&lt;/strong&gt; , we just had to upgrade axios, luckily it didn’t cause any breakage in functionality. Then there were some patches we were using, you can see the &lt;a href="https://github.com/ziaulrehman40/redux-token-auth/commits/develop"&gt;commit history&lt;/a&gt; on github for &lt;a href="https://github.com/ziaulrehman40/redux-token-auth/tree/develop"&gt;our final clone&lt;/a&gt; as well, but i will put in a summary here as well, to save you some time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Changes we got from our base clone:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Minimum required Node version from 8 to 10&lt;/li&gt;
&lt;li&gt;React from 15 to 16&lt;/li&gt;
&lt;li&gt;Redux from 3 to 4&lt;/li&gt;
&lt;li&gt;All other dependencies also upgraded including jest/typescipt etc (&lt;a href="https://github.com/kylecorbelli/redux-token-auth/compare/master...kazukinagata:develop?expand=1#diff-7ae45ad102eab3b6d7e7896acd08c427a9b25b346470d7bc6507b6481575d519"&gt;detail here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Also a lot of code refactor, majorly because of upgrades of packages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Further changes we made:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Axios upgrade (to fix security issue errors)&lt;/li&gt;
&lt;li&gt;General package upgrade (in a bid to make it last a bit longer)&lt;/li&gt;
&lt;li&gt;Fixes to &lt;a href="https://github.com/lynndylanhurley/devise_token_auth/issues/1053#issuecomment-854370966"&gt;remove a patch&lt;/a&gt; we were using prior to upgrades, as we use rotating tokens&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;All good?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not quiet,&lt;/strong&gt; we did this and used &lt;code&gt;"redux-token-auth": "git+https://github.com/ziaulrehman40/redux-token-auth.git#9812f831a84968dba593b22b3f48b50b322e2351"&lt;/code&gt; in our &lt;code&gt;package.json&lt;/code&gt; , but we bumped into &lt;a href="https://github.com/kylecorbelli/redux-token-auth/issues/56"&gt;this issue&lt;/a&gt;, for which we had to take &lt;a href="https://github.com/kylecorbelli/redux-token-auth/issues/56#issuecomment-494187365"&gt;this route&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All good now? Neh, Yet another issue&lt;/strong&gt; (and a small hack to fix it)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;redux-token-auth&lt;/code&gt; sets some data in redux under &lt;code&gt;reduxTokenAuth.currentUser&lt;/code&gt; key to help us identify if auth attempt is in progress, or has been completed and failed/succeeded. But default data points ( &lt;strong&gt;&lt;code&gt;isLoading&lt;/code&gt; and &lt;code&gt;isSignedIn&lt;/code&gt;&lt;/strong&gt; ) were not working as expected for some edge cases, after a lot of debugging(and nearly giving up on the whole custom clone thing), i found an un-documented data point, which helped us with everything, it is &lt;code&gt;hasVerificationBeenAttempted&lt;/code&gt;, using this and incorporating it in our checks fixed the issue. This is the only part which seemed a bit hacky, but it was to be used in only 1-2 places, so didn’t seem like a big issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next we moved to upgrading journey for other pieces of our app. And at the time of writing this, we have a final build ready of upgrades ready to go out in a couple of days. We will try to share more of our journey with you guys soon.&lt;/p&gt;

&lt;p&gt;If you have any feedback, comments or questions, don’t hesitate at all. Until next time, take care!&lt;/p&gt;

</description>
      <category>react</category>
      <category>uncategorized</category>
      <category>websites</category>
      <category>reactupgrade</category>
    </item>
    <item>
      <title>Devise gem force confirmation on email change and remove old email</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Thu, 09 Jan 2020 10:26:18 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/devise-gem-force-confirmation-on-email-change-and-remove-old-email-3oj</link>
      <guid>https://dev.to/ziaulrehman40/devise-gem-force-confirmation-on-email-change-and-remove-old-email-3oj</guid>
      <description>&lt;p&gt;Default behavior of devise when &lt;code&gt;confirmable&lt;/code&gt; in model and &lt;code&gt;reconfirmable&lt;/code&gt; in &lt;code&gt;devise.rb&lt;/code&gt; are enabled is that &lt;strong&gt;it does send a reconfirmation email email if email is changed, but it does not remove/replace previous email and does allow users to still login through old email while new email’s confirmation is pending.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I am on rails 6.0.2, Devise 4.7.1 for the record.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem?
&lt;/h3&gt;

&lt;p&gt;This is a bit odd behavior for some scenarios, like when users are being controlled by admins(lets say in tenants), &lt;strong&gt;sometimes we want to enforce email change and reconfirmation when admin changes the email&lt;/strong&gt; of any user. I could not find any official solution for this issue, and i had to go with monkey patching.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution:
&lt;/h3&gt;

&lt;p&gt;For this, i had to overwrite a method in my user model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # overwirte from /devise/models/confirmable.rb to force email re-confirmation for email changes
  def postpone_email_change_until_confirmation_and_regenerate_confirmation_token
    @reconfirmation_required = true
    self.unconfirmed_email = email
    # self.email = self.email_was
    self.confirmed_at = nil
    self.confirmation_token = nil
    generate_confirmation_token
  end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila, this replaces old email completely and forces a confirmation of that new email before users can continue using the system.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It took me a couple of hours to come to this solution after finding no official solution for this seemingly simple usecase, so logging for my record as well as for community. Happy coding!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>uncategorized</category>
    </item>
    <item>
      <title>Shopify-polaris form building with Formik, React-Final-Form and Yup</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Sun, 24 Nov 2019 13:56:40 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/shopify-polaris-form-building-with-formik-react-final-form-and-yup-30dd</link>
      <guid>https://dev.to/ziaulrehman40/shopify-polaris-form-building-with-formik-react-final-form-and-yup-30dd</guid>
      <description>&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Not a code guide of either of the mentioned tools, but rather my experience using these, the problems I faced and the solution we adopted.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;We opted &lt;a href="https://github.com/jaredpalmer/formik"&gt;Formik&lt;/a&gt; to build forms in our app, everything was going fine until we had a long dynamically generated form. With as few as ~100 fields, it started to lag a lot. Than I stumbled upon lot of issues Formik has (both open and closed) about speed issues and double renders, so decided to try something else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/final-form/react-final-form"&gt;React-final-form&lt;/a&gt; was next in the list, It turned out to be great choice with a lot of fine grained control options, speed was a lot better, we also enabled &lt;code&gt;validateOnBlur&lt;/code&gt;, which made it lighting fast. We had to build a few wrappers for &lt;a href="https://polaris.shopify.com/components/get-started"&gt;shipify-polaris components&lt;/a&gt; in use. You can find the code/s below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Long version
&lt;/h2&gt;

&lt;p&gt;We started building a shopify embedded app with &lt;code&gt;Rails 6&lt;/code&gt; and &lt;code&gt;ReactJS&lt;/code&gt;. I was using &lt;code&gt;react_rails&lt;/code&gt; gem for the purpose of binding the two.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;For those of you who don’t know, shopify provides a library of UI components among some guidelines to build shopify plugins/apps keeping the experience as close to original shopify experience as possible&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
As shopify plugins are rendered in an iframe, that means anyone can use whatever he wants for the UI/backend and pretty much everything. This can result in completely different UI components and their look and feel across different plugins. This is where shopify-polaris comes in to unify the user experience across different plugins.&lt;/p&gt;

&lt;p&gt;Shopify provides official react and html versions of this, but their guidelines can be followed independently of the UI framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  ENOUGH OF THE SHOPIFY, WHERE ARE FORMS!
&lt;/h2&gt;

&lt;p&gt;OHK! I hear you. Lets get right into forms.&lt;/p&gt;
&lt;h3&gt;
  
  
  Building forms with formik
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/jaredpalmer/formik"&gt;Formik&lt;/a&gt; is a nice small size library, &lt;strong&gt;it is supposed to support all common cases and does not present itself as one stop shop for all kind of form needs&lt;/strong&gt;. And they are right. We build small forms with relatively simple &lt;a href="https://github.com/jquense/yup"&gt;Yup&lt;/a&gt; validation schema and it was working great. There was no lag or anything like that when editing the form.&lt;/p&gt;

&lt;p&gt;Than we had to create a dynamic form which could have N sections, and each section will have minimum of ~35 fields.&lt;br&gt;&lt;br&gt;
&lt;em&gt;Building schema for this complex and conditional form was also interesting but that’s not topic here.&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Our normal form had around 100+ fields. When we did our development and testing on sample form of 1 section, everything was working fine. &lt;strong&gt;We used a small helping library &lt;a href="https://github.com/SatelCreative/formik-polaris"&gt;@satel/formik-polaris&lt;/a&gt; to bind shopify components and formik without any extra work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;After everything was built, when we tested this against real world loads of forms with 3-4 sections. It showed huge lag when editing. We would see text changing after a complete second of stopping the key-presses. This was obviously un-acceptable to deliver the feature. Here is when our debugging journey began.&lt;/p&gt;
&lt;h3&gt;
  
  
  Trying to fix lags while sticking with formik
&lt;/h3&gt;

&lt;p&gt;As I mentioned earlier, we had a complex schema with conditional logics, array of objects and so on. We knew this is one place where the bottleneck is, formik is validating whole form on a single key-press, but it did not justify this much of the lag.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;One other issue was multi rendering&lt;/strong&gt; , it was re-rendering whole form minimum of 2 times on single key press. Which of-course means a lot of CPU load.&lt;/p&gt;
&lt;h4&gt;
  
  
  Memoization
&lt;/h4&gt;

&lt;p&gt;Initially we suspected it is the re-renders which is causing the main issue, so &lt;strong&gt;we did split our components in smaller chunks with memoization in mind. And used &lt;code&gt;React.memoize&lt;/code&gt; to make them pure and stop their re-renders.&lt;/strong&gt; but despite moving a huge chunk of form to memoized versions, there was little to no effect on lag.&lt;/p&gt;
&lt;h4&gt;
  
  
  Try to reduce re-renders to 1
&lt;/h4&gt;

&lt;p&gt;There were multiple issues which we found during our debugging on formik about multiple re-renders, &lt;em&gt;with very few ever resolved&lt;/em&gt;, and that either didn’t help us anyway. So we were stuck with multiple re-renders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this point we were so frustrated&lt;/strong&gt; with this experience and saw number of open issues about speed on formik’s large forms that we were fully convinced that &lt;strong&gt;formik speed issue is a real thing&lt;/strong&gt; , and we need to move forward and try something else. This is when I saw a suggestion on an issue comment in formik to use react-final-form and we thought why not?&lt;/p&gt;
&lt;h3&gt;
  
  
  Replacing &lt;code&gt;formik&lt;/code&gt; with &lt;code&gt;react-final-form&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;React final form’s first impression from &lt;a href="https://final-form.org/react"&gt;docs&lt;/a&gt; and readme was that it is built to be one-stop-shop for all kind of forms, which means it has a lot of built in fine grained controls for all kind of use cases. And it is &lt;code&gt;async&lt;/code&gt; by default. Which means it runs validations async reducing any possibilities of lags due to validations.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;React-final-form&lt;/code&gt; even has &lt;a href="https://final-form.org/docs/react-final-form/migration/formik"&gt;a brief dedicated guide to migrate from formik&lt;/a&gt;. So i don’t need to add those details. I will only add details which are specific to shopify-polaris.&lt;/p&gt;

&lt;p&gt;So as we were using &lt;a href="https://github.com/SatelCreative/formik-polaris"&gt;@satel/formik-polaris&lt;/a&gt; which binds polaris components &lt;code&gt;onChange&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; type properties to formik.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;I could not find anything similar for &lt;code&gt;react-final-form&lt;/code&gt; which meant i had to write my own wrappers. Which are not a big deal but its always nice to have plug-able solutions instead of writing your own.&lt;br&gt;&lt;br&gt;
&lt;a href="https://gist.github.com/ziaulrehman40/f305669431eeb3876671e24ef23f821e"&gt;Here is a gist&lt;/a&gt;i created with code for the wrappers/adapters and notes on their usage.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Using Yup validation schema with &lt;code&gt;react-final-form&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;There is apparently no offical way to use a validation schema in &lt;code&gt;react-final-form&lt;/code&gt; while &lt;code&gt;formik&lt;/code&gt; had this support. I found a function somewhere in an issue on github. And that worked flawlessly for us, here is the final form of that function we used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;set&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash-es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// For extracting errors per field for formik&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;convertYupErrorsToFieldErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;yupErrors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;yupErrors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasOwnProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// And to use yup schema for validation:&lt;/span&gt;
 &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalFormYupValidator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;abortEarly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;convertYupErrorsToFieldErrors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And to use this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;finalFormYupValidator&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../helpers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Form&lt;/span&gt;
      &lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialValues&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;validate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;finalFormYupValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ValidationSchema&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Obviously you may tune above according to your needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Fine tuning &lt;code&gt;react-final-form&lt;/code&gt; for our usage
&lt;/h4&gt;

&lt;p&gt;As soon as we made switch to &lt;code&gt;react-final form&lt;/code&gt;, &lt;strong&gt;we saw immediate effect of at-least 4-5x speed improvement&lt;/strong&gt; , we could sense a little bit of lag still but it was a lot better already.&lt;br&gt;&lt;br&gt;
We decided to fix this lag too, so we explored other options. As our form was considerably big, we knew validations are what is causing this remaining lag. So we enabled &lt;code&gt;validateOnBlur&lt;/code&gt; option(by passing it as a prop to &lt;code&gt;Form&lt;/code&gt; and voila! Our form was as fast as it could get with no lag at all.&lt;/p&gt;

</description>
      <category>react</category>
      <category>uncategorized</category>
      <category>websites</category>
      <category>finalform</category>
    </item>
    <item>
      <title>Migrating from react-native-router-flux to react-router for web compatibility</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Sun, 25 Aug 2019 18:10:44 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/migrating-from-react-native-router-flux-to-react-router-for-web-compatibility-4oji</link>
      <guid>https://dev.to/ziaulrehman40/migrating-from-react-native-router-flux-to-react-router-for-web-compatibility-4oji</guid>
      <description>&lt;p&gt;Part of our effort to &lt;a href="https://dev.to/ziaulrehman40/part-1-converting-react-native-app-to-react-native-web-react-pwa-in-monorepo-architecture-4nhi-temp-slug-9666105"&gt;compile our react-native app for web, so we can have kind of a PWA built in react-native which runs on iOS, android and app, all 3 with same codebase.&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;We did this migration keeping the interface as close to &lt;code&gt;react-native-router-flux&lt;/code&gt; as we could so there are minimal required changes in the app&lt;/strong&gt; , and in some cases we were able to just change the import and that was it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Trying to avoid replacement
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;We were using&lt;code&gt;react-native-router-flux&lt;/code&gt; v4.0 in our RN(0.59.9)&lt;/strong&gt; app, this version is based on &lt;code&gt;react-navigation&lt;/code&gt; v2. When compiling for web, we obviously need compatible libraries. Although &lt;code&gt;react-navigation&lt;/code&gt; didn’t support web officially in v2, we gave it a try, many things were working, we could navigate just fine. &lt;strong&gt;but issue was, there was no url paths being changed while navigating(among some other issues)&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
We need to show urls to users so users can bookmark, share or do whatever they want with those urls.&lt;/p&gt;
&lt;h4&gt;
  
  
  Let’s try an upgrade?
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://reactnavigation.org/docs/en/web-support.html"&gt;&lt;code&gt;react-navigation&lt;/code&gt; v3 is officially supporting web now&lt;/a&gt;, although in early stages, there have been a lot of work for web support and efforts are still underway, &lt;a href="https://github.com/react-navigation/web"&gt;but progress on the web part seems a bit slow&lt;/a&gt;(at the time of writing) with lot of important use-ability related issues open with little to no efforts to resolve those issues. But as it was most easy to just try upgrading our existing library’s version, we gave it a try.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;react-native-router-flux&lt;/code&gt; v4.1(which is in beta, at the time of writing) uses v3 of &lt;code&gt;react-navigation&lt;/code&gt;, so we upgraded to that and issues were still there. Issues like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No URL paths, only domain name kept showing on all pages.&lt;/li&gt;
&lt;li&gt;Pages were not scrollable on web when bottom navigation was in use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So as odds were &lt;em&gt;heavily&lt;/em&gt; against this setup to work, we decided to just change the library and choose an alternative.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why &lt;code&gt;react-router&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;react-router&lt;/code&gt; is the most mature library which supports both native and web&lt;/strong&gt; , although packages for web and native are different but API is almost identical, which is a huge benefit, as it allows us to conditionally import(based on OS) and forget about the rest, as API is same(you will see this implemented in a minute).&lt;/p&gt;

&lt;p&gt;It is also a good choice for cross platform compatibility because unlike &lt;code&gt;react-navigation&lt;/code&gt; it only concerns itself with navigation, building UI for bottom or top or whatever navigations is not its job, that reduces parts which needs to be fixed for web and makes compatibility a lot easier.&lt;/p&gt;
&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;Before we jump into conversion, lets discuss some gotchas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Depending upon size of your application, &lt;strong&gt;this can be a lot of rework,&lt;/strong&gt; we did worked almost 2 weeks to set our app fully working with this new library.&lt;/li&gt;
&lt;li&gt;You &lt;strong&gt;can’t pass functions(callback etc) or any other serializable object in state for next route&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You will have to redo any non-routing features you are using from &lt;code&gt;react-navigation&lt;/code&gt;
(i am referring to &lt;code&gt;react-navigation&lt;/code&gt; as &lt;code&gt;react-native-router-flux&lt;/code&gt; is based on that and all features it gives us are based on those in &lt;code&gt;react-navigation&lt;/code&gt;).
What I mean by non-routing features are features like building UI for bottom navigation and anything like that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To keep the API same, we will use render props pattern&lt;/strong&gt; to spread initial props/state and even url params to the component being rendered on any specific route.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Actual Conversion
&lt;/h2&gt;

&lt;p&gt;First, add these packages: &lt;code&gt;react-router-dom&lt;/code&gt; and &lt;code&gt;react-router-native&lt;/code&gt;, make sure their versions match.&lt;/p&gt;

&lt;p&gt;Now, we will need to write a couple of special files, which will contain platform specific conditional imports etc. Then, we will use these files to import related components instead of importing from actual libraries. You can consider it kind of wrappers.&lt;br&gt;&lt;br&gt;
We will be taking an incremental approach, so we will not remove the router-flux in start, rather we will keep it until we are finished with our whole process.&lt;/p&gt;

&lt;p&gt;Let’s start with magic files(you can copy these as is).&lt;/p&gt;
&lt;h3&gt;
  
  
  Magic Files
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. &lt;code&gt;Router.js&lt;/code&gt; file:
&lt;/h4&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Platform } from 'react-native'; 

const isWeb = Platform.OS === 'web'; 

const RouterPackage = isWeb ? require('react-router-dom') : require('react-router-native'); 

export const Router = RouterPackage.Router; 
export default RouterPackage;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;_ &lt;strong&gt;Disclaimer:&lt;/strong&gt; This(and some others) code is taken from somewhere on the web(maybe it was a github issue or something)_.&lt;br&gt;&lt;br&gt;
Place this file at the location of your preference(we kept it at root, for now). This file is doing nothing fancy, it is just conditionally importing correct library, when its web, we import from &lt;code&gt;react-router-dom&lt;/code&gt; otherwise we import from &lt;code&gt;react-router-native&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. &lt;code&gt;RouteHistory&lt;/code&gt; files:
&lt;/h4&gt;

&lt;p&gt;For this, we needed to create two files, first &lt;code&gt;RouteHistory.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createMemoryHistory } from "history"; 
export default createMemoryHistory();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And for web another file, &lt;code&gt;RouteHistory.web.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createBrowserHistory } from "history"; 
export default createBrowserHistory();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you don’t know what this &lt;code&gt;.web.js&lt;/code&gt; extension is, and you are not working on a project which will compile to web, you can skip the web.js file. As, keeping it short, &lt;code&gt;.web.js&lt;/code&gt; file is used instead of &lt;code&gt;.js&lt;/code&gt; file on the web, when available. Moving on.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What these files are doing is just creating a history object&lt;/strong&gt; , which we will pass to &lt;code&gt;react-router&lt;/code&gt; so a proper history strategy is used as per platform, &lt;code&gt;memoryHistory&lt;/code&gt; on mobile and &lt;code&gt;browserHistory&lt;/code&gt; on web.&lt;/p&gt;

&lt;h3&gt;
  
  
  Routes Mapping
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Now we have our magic files in place, now we will start our actual conversion process.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For routes definition, i made 3 files:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. &lt;code&gt;urls.js&lt;/code&gt; file:
&lt;/h4&gt;

&lt;p&gt;This file is simplest, it just contain url definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export default {
    applyJob: '/job',
    browse:   '/job/:job_id'
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  2. &lt;code&gt;RouteActions.js&lt;/code&gt; file:
&lt;/h4&gt;

&lt;p&gt;This file will be used to navigate to different routes, it will replace &lt;code&gt;import { Actions } from 'react-native-router-flux'&lt;/code&gt; with something like: &lt;code&gt;import Actions from '../RouteActions'&lt;/code&gt;. And hence, will be on forefront for keeping the API same as router-flux’s Actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Platform } from 'react-native';
import history      from './RouteHistory';
import urls         from './urls.js';
import { generatePath } from "react-router";

export default class RouteActions {
  static pop() {
    setTimeout(() =&amp;gt; {
      history.goBack();
    }, 1);
    return null;
  }

  static base(componentProps = {}) {
    history.push('', componentProps);
  }

  static post(componentProps) {
    const url = generatePath(urls.post, {postId: componentProps.postId});
    history.push(url, componentProps);
  }

  static posts(componentProps = {}) {
    history.push(urls.posts, componentProps);
  }

  static refresh(componentProps = {}) {
    history.replace(history.location.pathname, componentProps);
  }

  static replace(route, componentProps = {}) {
    history.replace(route, componentProps);
  }

  static jump(route, componentProps) {
    history.push(route, componentProps);
  }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that there are quiet a few &lt;strong&gt;extra routes&lt;/strong&gt; , like &lt;code&gt;pop&lt;/code&gt;, &lt;code&gt;refresh&lt;/code&gt;, &lt;code&gt;replace&lt;/code&gt;, &lt;code&gt;jump&lt;/code&gt; and &lt;code&gt;base&lt;/code&gt;, these routes are implemented as our app was using these routes a lot from &lt;code&gt;react-native-router-flux&lt;/code&gt;, we tried to maintain as much interface as we could to match that of &lt;code&gt;Actions&lt;/code&gt; from &lt;code&gt;react-native-router-flux&lt;/code&gt;. And hence, we had to implement these functionalities.&lt;/p&gt;

&lt;p&gt;Secondly, we are receiving &lt;code&gt;componentProps&lt;/code&gt; and passing them in &lt;a href="https://github.com/ReactTraining/history#navigation"&gt;&lt;code&gt;push&lt;/code&gt; and &lt;code&gt;replace&lt;/code&gt; functions&lt;/a&gt;. This is also for the effort to match &lt;code&gt;Actions&lt;/code&gt; behavior in &lt;code&gt;react-native-router-flux&lt;/code&gt;, you will more details about this in &lt;code&gt;Routes.js&lt;/code&gt; file description.&lt;/p&gt;

&lt;p&gt;You might also have noticed that &lt;code&gt;componentProps&lt;/code&gt; is optional in all other routes, but not in &lt;code&gt;post&lt;/code&gt; function/route. Why?&lt;br&gt;&lt;br&gt;
This is because we are expecting an object &lt;strong&gt;will&lt;/strong&gt; be passed, which &lt;strong&gt;will&lt;/strong&gt; contain at-least &lt;code&gt;postId&lt;/code&gt;, and can contain any other props it want to pass to the component being rendered o the route.&lt;/p&gt;

&lt;p&gt;Take your time to try to make sense of this file. Then continue.&lt;/p&gt;
&lt;h4&gt;
  
  
  3. &lt;code&gt;Routes.js&lt;/code&gt; file:
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;This file will be a replacement of old &lt;code&gt;react-native-router-flux&lt;/code&gt; routes definitions&lt;/strong&gt; , whether that was in a separate file or mixed up in &lt;code&gt;App.js&lt;/code&gt;, this new file will replace those routes definitions and will actually be imported in &lt;code&gt;App.js&lt;/code&gt; to render routes replacing router-flux’s import.&lt;/p&gt;

&lt;p&gt;For the sake of simplicity, let’s assume there are only three routes we have to map:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/posts&lt;/code&gt; (is home page after signin)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/post/:post_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;empty/root route which renders home page&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It will look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, {Component} from 'react';
import Routing, { Router } from './wrappers/Router/Router';
import history from './RouteHistory';
import urls from './urls.js';

//Screens
import Posts from './screens/Posts';
import Post from './screens/Post';
import Home from './screens/Home';

const { Route, Switch, Redirect } = Routing;

class Routes extends Component {
    render() {
        return (
            &amp;lt;Router history={history}&amp;gt;
                &amp;lt;Switch&amp;gt;
                    &amp;lt;Route exact path='' component={Home}/&amp;gt;
                    &amp;lt;Route exact path={urls.posts}
                        component={(props) =&amp;gt; &amp;lt;Posts {...props} {...props.location.state}/&amp;gt;}
                    /&amp;gt;
                    &amp;lt;Route exact path={urls.post}
                        component={(props) =&amp;gt; &amp;lt;Post {...props} {...props.location.state}/&amp;gt;}
                    /&amp;gt;
                    &amp;lt;Redirect to="/" /&amp;gt;
                &amp;lt;/Switch&amp;gt;
            &amp;lt;/Router&amp;gt;
        );
    }
}

export default Routes;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We are importing our three screens and rendering them based on the url, lets break this down further:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Switch&lt;/code&gt; is a component which is used to render something based on some condition, it stops looking for matches whenever it finds first match, &lt;a href="https://reacttraining.com/react-router/web/guides/basic-components"&gt;you can read docs for more info on this.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we are on root path, without any sub paths, it will render &lt;code&gt;Home&lt;/code&gt; component, as you can see in the code snippet.&lt;/p&gt;

&lt;p&gt;If we are at &lt;code&gt;/posts&lt;/code&gt; route, it will render &lt;code&gt;Posts&lt;/code&gt; component, but that is a bit different, it is using &lt;a href="https://reactjs.org/docs/render-props.html"&gt;render props pattern&lt;/a&gt; here, let’s discuss why.&lt;br&gt;&lt;br&gt;
You can notice we are spreading a couple things while rendering component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;component={(props) =&amp;gt; &amp;lt;Post {...props} {...props.location.state} {...props.match.params}/&amp;gt;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is to match the &lt;code&gt;react-native-router-flux&lt;/code&gt;‘s interface for navigating(which is something like&lt;br&gt;&lt;br&gt;
&lt;code&gt;Actions.post({postId: 100, someOthrProps: 'value'})&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;{…props}&lt;/code&gt; : this is spreading default props from react-router, this includes &lt;code&gt;history&lt;/code&gt;, &lt;code&gt;location&lt;/code&gt; and &lt;code&gt;match&lt;/code&gt;(and maybe more). Please explore &lt;a href="https://reacttraining.com/react-router/web/guides/quick-start"&gt;docs&lt;/a&gt; for details.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{…props.location.state}&lt;/code&gt; this will spread any values we are passing when routing with &lt;code&gt;Actions.post&lt;/code&gt; just like &lt;code&gt;react-native-router-flux&lt;/code&gt; does, &lt;a href="https://github.com/aksonov/react-native-router-flux/blob/master/README2.md#usage"&gt;it calls this PARAMS in its docs&lt;/a&gt;. So from our example &lt;code&gt;Actions.post({postId: 100, someOthrProp: 'value'})&lt;/code&gt;, post component will receive two props &lt;code&gt;postId&lt;/code&gt; and &lt;code&gt;someOthrProp&lt;/code&gt;.
&lt;em&gt;This is same concept from router-flux, if you are here reading this doc on replacing flux, I assume you have worked on router-flux and will understand what I am talking about here.&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;
  
  
  Lets use this new router
&lt;/h4&gt;

&lt;p&gt;Now we can open our &lt;code&gt;App.js&lt;/code&gt; file and start using this router, something like this(it is simplified a lot, of-course):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, {Component} from 'react';
import { View } from 'react-native';
import Routes from './Routes';

export default class App extends Component {
    render() {
        return (
            &amp;lt;View&amp;gt;
                &amp;lt;Routes/&amp;gt;
            &amp;lt;/View&amp;gt;
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Not to mention, you need to remove &lt;code&gt;react-native-router-flux&lt;/code&gt;‘s import and initialization part in here.&lt;/p&gt;

&lt;h4&gt;
  
  
  Finally, replace imports:
&lt;/h4&gt;

&lt;p&gt;At this stage, all your routing will be broken, you will be going to base/home route just fine, and any links or redirections will throw errors, thats because we have removed flux initialization from &lt;code&gt;App.js&lt;/code&gt; and still trying to route using flux(in other components), lets start fixing our imports from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Actions } from 'react-native-router-flux'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Actions from '../RouteActions'; //fix path obviously
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And most routes should work, given that you have mapped all your urls in all 3 files, &lt;code&gt;urls.js&lt;/code&gt;, &lt;code&gt;Routes.js&lt;/code&gt; and &lt;code&gt;RouteActions.js&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
&lt;em&gt;(This 3 file structure is obviously optional.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Lookout and workout the gotchas and incrementally check and fix all links/redirects mapping routes in appropriate files.&lt;/p&gt;

&lt;h4&gt;
  
  
  Are we finished?
&lt;/h4&gt;

&lt;p&gt;Not if you have &lt;code&gt;deeplinking&lt;/code&gt; or universal links in your apps, in that case a little extra work might be required. We will not look into that here. you will have to figure that out yourself.&lt;/p&gt;

&lt;p&gt;And we are done!&lt;/p&gt;

&lt;p&gt;Thanks for bearing with me, do let me know if some parts in this article are hard to follow_(I understand there might be, but for someone who has worked properly with &lt;code&gt;react-native-router-flux&lt;/code&gt;, this should not be hard to understand, still I am open to suggestions for improvement.)_&lt;/p&gt;

&lt;p&gt;Cheers!&lt;/p&gt;

</description>
      <category>uncategorized</category>
      <category>reactpwa</category>
      <category>reactnative</category>
      <category>reactnativerouter</category>
    </item>
    <item>
      <title>Part 1 – Converting react native app to react-native-web (react PWA) in monorepo architecture</title>
      <dc:creator>Zia Ul Rehman</dc:creator>
      <pubDate>Sun, 28 Jul 2019 18:18:33 +0000</pubDate>
      <link>https://dev.to/ziaulrehman40/part-1-converting-react-native-app-to-react-native-web-react-pwa-in-monorepo-architecture-4no</link>
      <guid>https://dev.to/ziaulrehman40/part-1-converting-react-native-app-to-react-native-web-react-pwa-in-monorepo-architecture-4no</guid>
      <description>&lt;h3&gt;
  
  
  &lt;strong&gt;TL;DR&lt;/strong&gt; :
&lt;/h3&gt;

&lt;p&gt;This series is about my journey of &lt;strong&gt;converting and existing react native app to render on web with same(90%+) code&lt;/strong&gt;, using the brilliant &lt;a href="https://github.com/necolas/react-native-web"&gt;react-native-web&lt;/a&gt; project which&lt;a href="https://twitter.com/necolas/status/913877194199359488?lang=en"&gt;came out of twitter&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites:
&lt;/h3&gt;

&lt;p&gt;This article assumes you already have some experience with &lt;code&gt;reactjs&lt;/code&gt; and &lt;code&gt;react-native&lt;/code&gt;, you know how to build mobile builds of a RN codebase, and have them run in a simulator.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Disclaimer:&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I am mostly experienced in &lt;code&gt;Ruby on Rails&lt;/code&gt; full stack development with mostly focus on backend and DevOps, i have worked on and off on different JS frameworks, but &lt;em&gt;I am not an expert(yet) in &lt;code&gt;babel&lt;/code&gt; and &lt;code&gt;webpack&lt;/code&gt; configurations etc&lt;/em&gt;. So proceed with caution, and point out any improvements in the process in comments.&lt;/p&gt;

&lt;p&gt;I am not yet sure how many parts this series will take. Also my work of conversion of this app is still WIP, so who knows what and where that work ends up. But i will share my journey anyway.&lt;/p&gt;

&lt;p&gt;Also note,  we did not had any active android builds, so this series won’t be explaining steps involved with building android successfully. But you should be able to have them run in the similar way as i will explain for ios below.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can skip Credits and background section if you like and can jump to “_Let’s Just begin the actual stuff!&lt;/em&gt;” section._&lt;/p&gt;

&lt;h3&gt;
  
  
  Credits/Shoutouts:
&lt;/h3&gt;

&lt;p&gt;Before we begin our journey, i wanted to give credits to a couple of people who helped a lot on this journey, first one is the &lt;a href="https://dev.to/brunolemos"&gt;Bruno Lemos&lt;/a&gt;, i got the &lt;code&gt;react-native monorepo setup&lt;/code&gt; with initial &lt;code&gt;react-native-web&lt;/code&gt; setup from &lt;a href="https://dev.to/brunolemos/tutorial-100-code-sharing-between-ios-android--web-using-react-native-web-andmonorepo-4pej"&gt;his excellent blog post&lt;/a&gt;. And other one is &lt;a href="https://github.com/seahorsepip"&gt;Thomas Gladdines&lt;/a&gt;, he was so kind to help me through email with all queries i had in the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background:
&lt;/h2&gt;

&lt;p&gt;On a product i am working on, they have a &lt;strong&gt;fully functional react-native app released in iOS app store&lt;/strong&gt; (&lt;em&gt;It did not had android builds and releases, we are planning on that as well but thats not the topic here).&lt;/em&gt; We had its RN version recently &lt;strong&gt;upgraded from &lt;code&gt;56.x to 59.9&lt;/code&gt;&lt;/strong&gt; , the app &lt;em&gt;IS&lt;/em&gt; on app store, but with not a lot of users just yet, as it is a startup still trying to get kick-started with their initial contracts and everything. Anyway.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Suddenly one morning,&lt;/em&gt; we have an emergency meeting, and the project manager tells us that we have these X and Y clients whom we are going to partner with, and these are business critical deals. &lt;strong&gt;And the issue is, they both need a web app instead of the mobile app, that too ASAP. And we need to port our react-native app to web version within a week or so,&lt;/strong&gt; knowing we are team of only two devs. Yup! Just like that.&lt;/p&gt;

&lt;p&gt;And incidentally, both of us had not any experience of converting apps to web. So &lt;strong&gt;I was tasked to do some R&amp;amp;D and provide a feasible plan for this conversion with minimum friction and time requirements.&lt;/strong&gt; So as per pressure from the business side, we had to choose something where we have minimum to none learning curve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In a perfect world&lt;/strong&gt; , we will just be putting our code through some code convertor which coverts our &lt;code&gt;react-native&lt;/code&gt; app to &lt;code&gt;reactjs&lt;/code&gt; web app. &lt;strong&gt;But we don’t live in a perfect world, do we?&lt;/strong&gt; Turns out, react community is making great progress towards hybrid apps and PWAs but it is not yet quiet there, &lt;a href="https://ionicframework.com/blog/announcing-the-ionic-react-beta/"&gt;ionic has its react beta out&lt;/a&gt;, there is a project called &lt;a href="https://github.com/microsoft/reactxp"&gt;ReactXP&lt;/a&gt; from microsoft and &lt;a href="https://www.google.com/search?client=opera&amp;amp;q=expo+react+web&amp;amp;sourceid=opera&amp;amp;ie=UTF-8&amp;amp;oe=UTF-8"&gt;expo is also working on web compatibility of its apps&lt;/a&gt;. It almost felt like we needed this conversion a couple of years too early.&lt;/p&gt;

&lt;p&gt;So keeping in mind our short time notice and business criticality, we just decided to keep our learning curve low and not worrying too much about future and &lt;strong&gt;to use &lt;a href="https://github.com/necolas/react-native-web"&gt;react-native-web,&lt;/a&gt; which currently officially supports Rn 0.55, and we are on 0.59&lt;/strong&gt;, as we had saw some people mention they are successfully running RN-web on RN 0.59, some mentioned some hacks &lt;a href="https://github.com/necolas/react-native-web/issues/1172#issuecomment-472196873"&gt;like making &lt;code&gt;react-native-web&lt;/code&gt; think its RN 0.55 when compiling for web&lt;/a&gt;. So we just decided to go with RN-web to get something running on web ASAP.&lt;/p&gt;

&lt;p&gt;I had to do a few re-dos to make it all work, thats why i thought it worths to write about it in detail, so people are not stuck where i did stuck and had to re-setup everything in a hope it will work, kind of restart &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zCyXRrdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f642.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zCyXRrdx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f642.png" alt="🙂"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintainability and future?
&lt;/h2&gt;

&lt;p&gt;This is a tough question, there is active progress on RN web, although it doesn’t look very active as far as commits go, and its having hard time keeping up with latest RN versions, but as more and more people start using this, more and more people will collaborate and participate in its development, and we should see better future.&lt;/p&gt;

&lt;p&gt;The monorepo architecture itself is quiet amazing and works flawlessly with &lt;code&gt;RN&lt;/code&gt;(except the issues of linking), but only issue we can foresee with &lt;code&gt;react-native-web&lt;/code&gt; is its active development. Its great to try out and get something running. But if you face too many issues and/or missing features you can’t live without, and you also happen to have lot of time to transition to &lt;code&gt;ionic&lt;/code&gt; or &lt;code&gt;ReactXP&lt;/code&gt; or anything like that, you can try those out. But as I mentioned, those are also not yet that mature, so good luck with that as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Just begin the actual stuff!
&lt;/h2&gt;

&lt;p&gt;Before starting, i should stress on importance of version control, just keep committing every small step which worked with proper commit messages, and you will save a lot of time.&lt;/p&gt;

&lt;p&gt;Ok, here we go.&lt;/p&gt;

&lt;h3&gt;
  
  
  Initial monorepo setup:
&lt;/h3&gt;

&lt;p&gt;As i mentioned above, i am using the monorepo architecture to share code between mobile and web, and i followed &lt;a href="https://dev.to/brunolemos/tutorial-100-code-sharing-between-ios-android--web-using-react-native-web-andmonorepo-4pej"&gt;an excellent blog post&lt;/a&gt; on the matter. There is a &lt;a href="https://github.com/brunolemos/react-native-web-monorepo"&gt;boilerplate repo&lt;/a&gt; also linked in the article, which you can use. But i preferred to setup this whole thing from scratch as described in the post i linked, so have a better sense of understanding of whats actually going on. Turns out, there is a lot going on.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BTW, there is an &lt;a href="https://medium.com/@mattklein123/monorepos-please-dont-e9a279be011b"&gt;interesting debate of &lt;code&gt;monorepo vs multi-repo&lt;/code&gt; architecture,&lt;/a&gt; we are not going that route. But it worths mentioning, when we are using something like RN-web, it makes most sense to have this architecture. Maybe not that much if we were just sharing the services and reducers etc among web and native react.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Ok, now we have our monorepo setup, with basic boilerplate running on both mobile and web. &lt;strong&gt;Let’s start importing our existing mobile app to this architecture.&lt;/strong&gt; Before we do that, i must mention that you can try converting the existing app to monorepo setup in place, but that didn’t work for us, was taking too much time that we decided to just port it over.&lt;/p&gt;

&lt;h3&gt;
  
  
  Little walkthrough of the monorepo setup
&lt;/h3&gt;

&lt;p&gt;During the setup from above guide(which you have to do from there, i will not be doing that all again over here), you would have noticed we are using &lt;code&gt;yarn&lt;/code&gt;, and its &lt;code&gt;workspaces&lt;/code&gt; feature. We have a folder called &lt;code&gt;packages&lt;/code&gt; on the root, it contains currently 3 sub-folder, each with their own &lt;code&gt;package.json&lt;/code&gt; file. but important part is, all the packages will be installed in the &lt;code&gt;node_modules&lt;/code&gt; folder at the root. Not in sub &lt;code&gt;node_modules&lt;/code&gt; folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lets go through these packages/folders one by one:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1- &lt;code&gt;Components&lt;/code&gt;:&lt;/strong&gt; This folder will hold generic components or the shared code so to speak, in this component we will have everything that we want to share. You can get creative with the naming if you like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2- &lt;code&gt;Mobile&lt;/code&gt;:&lt;/strong&gt; As name suggests, this folder will hold our mobile specific code. And when working/building mobile we will be staying in this folder. This folder further has the usual folders we see in any react-native app, the &lt;code&gt;ios&lt;/code&gt; &lt;code&gt;android&lt;/code&gt; &lt;code&gt;src&lt;/code&gt; folders etc. You should already know what are those and how we use them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3- &lt;code&gt;Web&lt;/code&gt;:&lt;/strong&gt; This is where actual web magic happens. This is supposed to be focus of this article.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gotchas!
&lt;/h3&gt;

&lt;p&gt;Just like there is no free lunch, there are a few issues I have came across. I am pasting first 3 straight from Bruno’s original article.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;react-native-web&lt;/code&gt; supports most of the &lt;code&gt;react-native&lt;/code&gt; API, but a few pieces are missing like &lt;code&gt;Alert&lt;/code&gt;, &lt;code&gt;Modal&lt;/code&gt;, &lt;code&gt;RefreshControl&lt;/code&gt; and &lt;code&gt;WebView&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If you come across a dependency that doesn’t work well with the monorepo structure, you can add it to the &lt;a href="https://github.com/devhubapp/devhub/blob/2f64088b84611f7458d3dd7933baf25ffb5196d9/package.json#L16-L18"&gt;nohoist&lt;/a&gt; list;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react-native link&lt;/code&gt; might not work well with monorepo projects without &lt;code&gt;nohoist&lt;/code&gt;; to workaround this, use &lt;code&gt;nohoist&lt;/code&gt; on &lt;code&gt;**/react-native&lt;/code&gt; OR instead of installing the dependencies only using &lt;code&gt;yarn workspace mobile add xxx&lt;/code&gt;, install them in the root directory as well: &lt;code&gt;yarn add xxx -W&lt;/code&gt;. Now you can link it and then later remove it from the root &lt;code&gt;package.json&lt;/code&gt;.
&lt;em&gt;(So far what I have been doing is the second option from this. Which is to copy dependencies to root &lt;code&gt;packakeg.json&lt;/code&gt;and later remove them from there.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Lookout for library versions when you copy the dependencies from existing app, they &lt;em&gt;WILL&lt;/em&gt; change and may jump to latest versions without you noticing if you don’t &lt;strong&gt;properly lock them in&lt;/strong&gt; &lt;code&gt;package.json&lt;/code&gt;, so i recommend you spend some time locking them properly and than making sure everything works in old app, before starting to port it.&lt;/li&gt;
&lt;li&gt;Be prepared to have some details of the functionality of your mobile app to be compromised, at-least for short term. As odd as it sounds, its a reality I faced, some RN components are not even supported in RN-web, one of them is &lt;code&gt;Alert&lt;/code&gt;, which we happen to use a lot, so we will need some patch(if we can find any) to make it work or we will have to use something else to achieve the same functionality.&lt;/li&gt;
&lt;li&gt;Unless you decide to limit the width of your web app, &lt;code&gt;you will probably have to fix a lot of responsiveness issues&lt;/code&gt;. And for some components/screens of your app. It may even feel like re-writing the view layer. As you can imagine the drastic changes for responsiveness.&lt;/li&gt;
&lt;li&gt;I have not yet setup some proper &lt;code&gt;versioning script&lt;/code&gt; like the one we were using in the old setup, but that might be a bit of problem for us. And we may need something custom for that, unless we can live without this and waste time every-time we need to release a new version.&lt;/li&gt;
&lt;li&gt;Lastly, as of point 4, 5 and 6, you can already guess, this RN and RN-web combo will work best for new react apps aiming for PWA or hybrid results, so to speak. This doesn’t mean it doesn’t worth a shot when you are short on time and want something out on web from existing RN codebase.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Importing existing app to this new setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Let’s start with dependencies
&lt;/h3&gt;

&lt;p&gt;I decided to first have dependencies moved, installed, and &lt;code&gt;yarn.lock&lt;/code&gt; file properly updated and than copy actual code. So i did that. I copied everything under &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt; sections in old &lt;code&gt;package.json&lt;/code&gt;(except react and react-native, any any other duplicates), and moved it over to &lt;code&gt;packages/components/package.json&lt;/code&gt; sections respectively. &lt;em&gt;(Note that I was not careful about the gotcha number 4 i mentioned above, that caused number of issues for us later on, so lookout for that).&lt;/em&gt; After i got everything installed and mobile app was still running fine(of-course, because we haven’t imported anything from these new dependencies), i made a new commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Now import actual code
&lt;/h3&gt;

&lt;p&gt;This particular step may sound like an easy thing, but it’s actually not. Believe me, I spent at-least one and a half of day to make it build for mobile in this new architecture! Yup…&lt;/p&gt;

&lt;p&gt;To import old code, i will explain what strategy i took, and you can decide what works best for you. I decided to copy whole existing code to &lt;code&gt;components&lt;/code&gt; package of the app, which is for the shared code. Idea was to first build the mobile successfully in this architecture, then try on web, and move what needs to be moved to mobile or web specific sub-packages.&lt;/p&gt;

&lt;p&gt;So i just copied everything from my old src folder to &lt;code&gt;packages/components/src&lt;/code&gt; and in our old setup, we had &lt;code&gt;App.js&lt;/code&gt; outside of the &lt;code&gt;src&lt;/code&gt;, i moved that inside of the &lt;code&gt;src&lt;/code&gt; as well, and had to update some import paths in &lt;code&gt;App.js&lt;/code&gt; but that’s fine. Now app should work? No, don’t forget linking and other &lt;code&gt;xcode&lt;/code&gt; specific settings your dependencies may require.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linking libraries and &lt;code&gt;xcode&lt;/code&gt; fixes for our app
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;There might be a better way to do this than what i am going to describe by my experience.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As mentioned in point number 3 of the gotchas section, linking is a bit tricky. Before linking though, &lt;strong&gt;if you use &lt;code&gt;cocoa pods&lt;/code&gt;, please setup those. Even if you don’t, this might be a good time to use them. Install cocoa pods&lt;/strong&gt;. When it looks good, choose how would you like to link the libraries, as described in above mentioned section, for now, what i am doing is copying over all dependencies from &lt;code&gt;packages/components/package.json&lt;/code&gt; to main &lt;code&gt;package.json&lt;/code&gt;, yarn install and run &lt;code&gt;react-native link&lt;/code&gt; from main directory. Then remove these dependencies from main package.json and yarn install. Last step, obviously, inside &lt;code&gt;ios&lt;/code&gt; directory run &lt;code&gt;pod install&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I had some complications&lt;/strong&gt; , which turned out to be caused by something else, which i will explain in a minute, but on this stage, what i did was to &lt;em&gt;copy over my old &lt;code&gt;podfile&lt;/code&gt; completely&lt;/em&gt;. I mean if you have a &lt;code&gt;podfile&lt;/code&gt;, it didn’t hurt to copy old one and just fix the references to the &lt;code&gt;node_modules&lt;/code&gt; folder present in main directory of the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;At this step, you can try and run your &lt;code&gt;.scworkspace&lt;/code&gt; file of your project, clean the build have the &lt;code&gt;metro bundler&lt;/code&gt; running in the background&lt;/strong&gt; (as described in the monorepo setup guide i linked command is something like: &lt;code&gt;yarn workspace mobile start&lt;/code&gt;), and &lt;strong&gt;let’s try building the app&lt;/strong&gt;. If you are lucky enough, your app will run, mine didn’t. It did built successfully, but it failed when loading files from metro bundler.&lt;/p&gt;

&lt;h4&gt;
  
  
  Strange &lt;code&gt;path&lt;/code&gt; and &lt;code&gt;fs&lt;/code&gt; errors
&lt;/h4&gt;

&lt;p&gt;First i had &lt;strong&gt;&lt;code&gt;bundling failed: Error: Unable to resolve module&lt;/code&gt;path&lt;code&gt;...&lt;/code&gt;&lt;/strong&gt; error, i blindly added a package named something path(you can google it up, i don’t remember, maybe it was named just path). Then i started seeing &lt;code&gt;bundling failed: Error: While trying to resolve module&lt;/code&gt;fs&lt;code&gt;from file...&lt;/code&gt; and &lt;code&gt;node_modules/fs/package.json&lt;/code&gt; was successfully found. However, this package itself specifies a &lt;code&gt;main&lt;/code&gt; module field that could not be resolved&lt;code&gt;thats when i thought maybe, **it is something wrong with my config** , not my dependencies, because i have all my dev and other dependencies installed same as in previous app. So it is not the dependencies i need but something else. What is turned out to be? _I missed to copy babel presets from old&lt;/code&gt;babel.config.js` to new one in mobile package. &lt;strong&gt;And that solved both of these issues.&lt;/strong&gt; _&lt;/p&gt;

&lt;p&gt;That was the part where I wasted a lot of my time. after this, i retried the build, low and behold! another error of-course &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qgur0s-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f600.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qgur0s-_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f600.png" alt="😀"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;react-native native module cannot be null&lt;/code&gt; errors
&lt;/h4&gt;

&lt;p&gt;This error was appearing after loading from metro bundler, turns out, &lt;strong&gt;it is complaining about missing proper linking&lt;/strong&gt;. As some libraries don’t just require you to run &lt;code&gt;react-native link&lt;/code&gt; they sometimes involve manual steps as well, like the &lt;code&gt;react-native-permissions&lt;/code&gt; package. So i had two options at this stage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go through all my dependencies readme, and make sure we have everything setup as needed.&lt;/li&gt;
&lt;li&gt;Match the old &lt;code&gt;.xcworkspace&lt;/code&gt; file libraries and linked section and make sure we have everything matching up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the shortage of time, i took route 2, i don’t recommend it, especially if you have android builds active as well. This took me some time to make sure we have everything we need. And after a few rounds of failure with similar errors, i was able to run the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; &lt;em&gt;This is the section i am almost sure about that this would have some better way of doing this than the above mentioned two, definitely better than the approach I took. Comment if you can suggest anything, and i will add it in the list for readers.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Boom! Mobile app is running in monorepo setup!
&lt;/h3&gt;

&lt;p&gt;There might be some other bits according to your setup, but that was it for me and i had my mobile build running! Finally!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PS:&lt;/strong&gt; As i mentioned, i had not properly locked my dependencies, i had some issues in the app at this point, but it was working, and our only goal was to have a web version ready ASAP. So we just took note of those issues and moved on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P8D22pFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ziatechblog.files.wordpress.com/2019/07/download.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P8D22pFE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://ziatechblog.files.wordpress.com/2019/07/download.jpeg" alt="download"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, at-least for part one. We have our mobile app running in this architecture. We can go from here in the next part and actually start porting for web app.&lt;/p&gt;

&lt;p&gt;Please share your experiences and anything you want to add/fix in the article.&lt;/p&gt;

&lt;p&gt;Till next time, TC.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ziatechblog.wordpress.com/2019/08/18/part-2-converting-react-native-app-to-react-native-web-react-pwa-in-monorepo-architecture/"&gt;Part 2 can be found here.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>uncategorized</category>
      <category>websites</category>
    </item>
  </channel>
</rss>
