amanhimself.dev Aman Mittal I'm Aman Mittal (@amanhimself). Software Developer and Tech Writer. Welcome to my blog! Generated on 2025-10-29T10:18:27.856Z Contains the markdown content of each blog post. --- ## 21 Useful Open Source Packages for React Native Slug: 21-useful-open-source-packages-for-react-native ![cover_image](https://i.imgur.com/nbq2XcZ.png) We live in the world of a variety, yet mobile devices are dominated by two major platforms, iOS and Android. It is a two-horse race, but that doesn’t make mobile app development _easy_. For iOS you write code using Objective-C or Swift. For Android, you use Java. In addition to different programming languages, the tool chains are entirely different too for both of these mobile platforms. To create app that work across devices, many modern day developers build Hybrid apps using HTML, CSS and JavaScript — as you would a web page— wrapped in native container. This way, you use (almost) one set of source code for developing applications for both iOS and Android . In recent years, hybrid frameworks have evolved from web view to use native APIs. This cross-platform approach of developing a mobile application comes with its own pros and cons. Pros such as being less-time consuming and cost-effective, and cons include performance issues. One of the great options that fall under the umbrella of cross-platform development is React Native. React Native was developed by Facebook and used by others such as [Tesla Motors](https://medium.com/u/24413768aadb), [Walmart Labs](https://medium.com/u/c884135151a4), [Uber](https://medium.com/u/b97b1b381b5a), [Instagram Engineering](https://medium.com/u/a4c6efa67fe0), [Discord](https://medium.com/u/fddf6af2df19), [Wix](https://medium.com/u/2741d9d88322) and so on. In a nutshell, React Native allows you to build mobile applications that look, feel and perform much more like native applications. The good thing for developers is that they can use almost the same concepts that are being used for building web applications. The list below contains an overview of the top open source libraries that you can use in your React Native application. > [**Also, try out the Crowdbotics application builder to instantly scaffold and deploy a React Native app.**](https://app.crowdbotics.com/dashboard/?utm_campaign=cb-medium&utm_source=blog-post&utm_medium=Medium&utm_content=react-native) ### USEFUL OPEN SOURCE REACT NATIVE PACKAGES ### lottie-react-native Lottie is a mobile library for Android and iOS that parses Adobe After Effects animations exported as JSON with `bodymovin` and renders them natively on mobile. With over 10k+ stars, this npm module helps you use community/custom build animations in your React Native application. [**react-native-community/lottie-react-native**](https://github.com/react-native-community/lottie-react-native) ### react-native-vector-icons `react-native-vector-icons` is the go to library if you are considering to use customizable icons with support for NavBar/TabBar, image source and full styling. This npm module bundles famous icon libraries like: - FontAwesome - IonIcons - EvilIcons - AntDesign - MaterialIcons - Octicons and many more. It is like have best of all the libraries in one place, and you do not have to go through the process of hooking up multiple libraries and then linking them with the React Native app. It also supports animation with React Native’s animation library, `Animated`. [**oblador/react-native-vector-icons**](https://github.com/oblador/react-native-vector-icons) ### react-native-gifted-chat Chat applications are a huge part of mobile app development. There are scenarios in which either you build complete chat applications or add it as a feature to your existing app. In both cases, this module is out there to help you get started with the UI. This npm module comes with fully customizable components, dates, multiple TextInput options, Redux support and so on. [**FaridSafi/react-native-gifted-chat**](https://github.com/FaridSafi/react-native-gifted-chat) ### react-native-image-picker An essential library for any app with Image upload or Image processing. It supports features like selecting from the gallery, and taking a photo from the camera. Another useful feature in this library that I like is the option to select the quality of an image you want to choose. This feature solves memory issues due to high-resolution images. [\*_react-native-community/react-native-image-picker_](https://github.com/react-native-community/react-native-image-picker) ### react-native-progress Showing progress of loading or any other action is important in an app. This library makes it easy to show progress by supporting 5 different components like Linear progress bar, circular, pie and so on using ReactART. ```js import * as Progress from 'react-native-progress'; ``` [**oblador/react-native-progress**](https://github.com/oblador/react-native-progress) ### Nativebase NativeBase is a sleek, ingenious, and dynamic front-end framework to build cross-platform Android and iOS mobile apps using ready-to-use generic components of React Native. What is really great about NativeBase is that you can use shared UI cross-platform components, which will drastically increase your productivity. Its documentation provides an in-depth specification on each components and customize them. You need a component library like Nativebase while working solo, or quickly prototyping an MVP or if you want to focus on the functionality of your application. [**GeekyAnts/NativeBase**](https://github.com/GeekyAnts/NativeBase) ### react-navigation Navigation has been a controversial topic in React Naive community, until `react-navigation` package has started to mature. With version `3` recently launched, this npm module is right now a complete solution provider for managing screens in a React Native application. It offers - stack navigation - tab navigation - drawer navigation - custom navigation support - Redux support for complex applications If you want to try it out, here is cool [example app](https://expo.io/@react-navigation/NavigationPlayground) built using it. [**react-navigation/react-navigation**](https://github.com/react-navigation/react-navigation) ### react-native-navigation React Native Navigation provides 100% native platform navigation on both iOS and Android for React Native apps. Developed and maintained by the team at Wix, is the second most commonly used package to support navigation of screens in a React Native app after `react-navigation`. The reason this package is often a second preference in the community is because of its set up process. You will have to manually hook this library with iOS build and Android `gradle` every time you want to use it by following a number of steps. ### react-native-languages A community package, react-native-languages is a library that helps you integrate the i18n-js library in a React Native application to internationalize and localize the application. With that, it has many utility functions that you can leverage. For example, to get the current device’s language, you would write the following code. ```js import RNLanguages from 'react-native-languages'; // Get Current device language console.log('language', RNLanguages.language); ``` [**react-native-community/react-native-languages**](https://github.com/react-native-community/react-native-languages) ### react-native-billing This library is exclusively to be used with React Native and Android. Use this library when you need to add in-app billing to your app for Android devices. The tool has a simple interface and works as a bridge by wrapping anjlab’s `InApp Billing` library. This library is up to date and supports ES6 features like `async/await`. ```js import InAppBilling from "react-native-billing"; async purchase() { try { await InAppBilling.open(); const details = await InAppBilling.purchase("android.test.purchased"); console.log("You purchased: ", details); } catch (err) { console.log(err); } finally { await InAppBilling.close(); } } ``` [**idehub/react-native-billing**](https://github.com/idehub/react-native-billing) ### react-native-iap This is a react-native link library project for in-app purchase for both Android and iOS platforms. The goal of this project is to have similar experience between the two platforms for in-app-purchase. It has a vast variety of helper functions that you can use. Android as a platform has more functions for in-app-purchase. [**dooboolab/react-native-iap**](https://github.com/dooboolab/react-native-iap) ### tcomb-form-native Forms can be a lot more complicated than icons or components as they have a lot of different parts and there’s logic involved when it comes to field validation and form submission. With this library, you simplify form processing immensely . It has a variety of configuration that is platform specific. Using this library you will be writing a lot less code, get usability and accessibility, and no need to update forms when the domain model changes. [**gcanti/tcomb-form-native**](https://github.com/gcanti/tcomb-form-native) ### Formik Handling forms is one of the most important aspect of being a good web developer. Same applies if you are using React Native for developing a mobile application. It is a small library that helps you to create forms in React and facilitates form building. It allows you to get values in and out of a form state, validate and get error messages, and effectively submit forms. [**jaredpalmer/formik**](https://github.com/jaredpalmer/formik) ### Redux Redux plays a huge part in React and React Native’s ecosystem when it comes to manage state in an application. Redux helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test. Using Redux, you can query, select, insert, and update a record in the database. Redux also has a really useful feature to edit live code. Redux works with any UI layer, and has a large ecosystem of add ons to fit your needs. [**reduxjs/redux**](https://github.com/reduxjs/redux) ### redux-form Another well maintained library for building forms in a React Native application. Along with managing state with Redux, this library allows you to track common form states as focused field, fields in the form, fields that the user has interacted with, field values, and many others. [**erikras/redux-form**](https://github.com/erikras/redux-form) ### redux-persist Redux Persist takes your Redux state object and saves it to persisted storage. Then on app launch it retrieves this persisted state and saves it back to redux. Managing user data when locally storing in a mobile device can be hard when data sets become complex. Using React Native API `AsyncStorage` natively can be difficult for large applications. [**rt2zz/redux-persist**](https://github.com/rt2zz/redux-persist) ### React Native Debugger React Native Debugger is standalone application that can be installed on your local machine for debugging a React Native application. As a developer, having a quality debugging environment can lead to be more productive, while helping you track down bugs and creating new features. Another advantage of using this standalone application, is that it already includes Redux DevTools by default. So if your application is depending on Redux state management library, with minimum configuration, you can hook up your React Native app. [**jhen0409/react-native-debugger**](https://github.com/jhen0409/react-native-debugger) ### React Native Firebase React Native Firebase is lightweight JavaScript library that helps you connect your React Native app to the native Firebase SDK for both iOS and Android platform. This process aims to mirror the official Firebase SDK as closely as possible. Even though the official SDK works with React Native, this package allows you to consume device SDKs which don’t exist on the Firebase JS SDK. To consume the official SDK in React Native, you will to opt for the web one. Things like AdMob, Analytics, Cloud Messaging (FCM), Remote Config, Performance Monitoring, Dynamic Links are not available in the official Firebase SDK. [**invertase/react-native-firebase**](https://github.com/invertase/react-native-firebase) ### Jest Jest is a unit testing framework created by Facebook and released on GitHub. It tests JavaScript code. Jest is a versatile testing tool with the ability to adapt to any JavaScript library or framework. Its advantages include snapshot testing support. [**Jest · _🃏 Delightful JavaScript Testing_**](https://jestjs.io/) ### Enzyme Enzyme is a testing tool from [AirbnbEng](https://medium.com/u/ebe93072cafd). It supports shallow, full DOM, and static rendering. Enzyme also offers developers API wrappers that are supposed to make asserting, manipulating, and traversing the React DOM easier. Another great benefit of the tool is that it is compatible with other testing libraries and frameworks including Jest and Mocha. [**airbnb/enzyme**](https://github.com/airbnb/enzyme) ### Detox The most difficult part of automated testing on mobile is the tip of the testing pyramid is E2E.Detox is End to End (_E2E_) testing library for applications written in React Native. It means testing application like a real user, but automatically with code. You will write code and this testing library provides tools to _click through_ the application like a real human user. For example, a test for a login screen in Detox as it runs on a device/simulator like an actual user looks like below: ```js describe('Login flow', () => { it('should login successfully', async () => { await device.reloadReactNative(); await expect(element(by.id('email'))).toBeVisible(); await element(by.id('email')).typeText('john@example.com'); await element(by.id('password')).typeText('123456'); await element(by.text('Login')).tap(); await expect(element(by.text('Welcome'))).toBeVisible(); await expect(element(by.id('email'))).toNotExist(); }); }); ``` [**wix/Detox**](https://github.com/wix/Detox) ### react-native-mock This third-party solution is relatively new. React-native-mock helps developers work with the latest versions of React Native. The library was specifically designed to facilitate testing of React Native apps. [**RealOrangeOne/react-native-mock**](https://github.com/RealOrangeOne/react-native-mock) ### ESLint Lastly, I leave you with the go to linting library used by almost every JavaScript developer. It is called ESLint. It is a pluggable linting utility for JavaScript and to let programmers discover issues with their JavaScript code before executing it. One great benefit of ESLint is that it gives developers the opportunity to create their own linting rules. I personally prefer to use rules provided by the team at AirBnb with some tweaks of my own. [**ESLint—Pluggable JavaScript linter**](https://eslint.org/) ### Conclusion There are other libraries that modules available for React Native for different purposes. Expect more in future since mobile development is hard when it comes to accessing to different APIs. Libraries such as _axios_ for network calls and _Apollo Client_ to query [GraphQL APIs](https://medium.com/crowdbotics/creating-a-graphql-server-with-nodejs-ef9814a7e0e6) can also be used with React Native, as they are used with React JS. I didn’t think that they are worth mentioning here in detail. I hope the above list provides you ready made solutions to help you build better React Native applications. [Originally published at Crowdbotics](https://medium.com/crowdbotics/21-useful-open-source-packages-for-react-native-807f65a818a1) --- ## Top open source libraries for Node.js Slug: 29-useful-open-source-libraries-for-nodejs ![cover_image](https://i.imgur.com/Yv3yfrm.png) > [Originally published at Crowdbotics](https://medium.com/crowdbotics/29-useful-open-source-libraries-for-nodejs-4cefe08f7205) [**Node.js**](https://nodejs.org) has become more and more popular as a framework because provides quick and efficient solutions for back-end development and integrates well with front-end platforms. Created by Ryan Dahl in 2009, Node.js is actively maintained by a large community as an open source project. It enables software and app developers to build fast and scalable web applications using just a few lines of code. [Node.js Foundation](https://medium.com/u/96cd9a1fb56) The world of custom software development constantly evolves with new trends, techniques, and languages. But, with Node.js, app development is significantly simplified. In this article, I collated a list of the useful open source libraries that you can use in your upcoming Node.js project. > [**Also, try out the Crowdbotics App Builder to instantly scaffold and deploy a NodeJS app.**](https://app.crowdbotics.com/create/?utm_campaign=cb-medium&utm_content=node-js) ### ExpressJS Express.js is a go-to, minimalist framework for Node.js web applications. In recent years, it has been a go to framework to write server side code for the applications that want to use and leverage Node.js. It is actively maintained by a great community, now supports almost all ES6 features and is used by both big companies and startups. There is no shortage of web frameworks when it comes to Nodejs and Express has survived the popularity phase so far. [**expressjs**](https://github.com/expressjs) ### AdonisJS It is a complete MVC Nodejs framework (_other than Sails_) that runs on all major operating systems without a problem. . It offers a stable ecosystem to write server-side web applications so you can focus on business needs over finalizing which package to choose or not. It differs from other Nodejs web frameworks such as Express and Koa in manner that those frameworks are mostly routing libraries with thin layer of middleware on top AdonisJS is combination of multiple packages that work together gracefully integrate with the application. For example, it provides a built-in ORM that is works well with SQL databases such as Postgres and MySQL. It helps to create efficient SQL- queries and is based on active record idea. Its query builder is easy to learn and allows us to build simple queries quickly. [**adonisjs/adonis-framework**](https://github.com/adonisjs/adonis-framework) ### MomentJS The standard JavaScript API already comes with the Date object for working with dates and times. However, this object is not very user-friendly when it comes to printing and formatting dates. In recent years, MomentJS has become a go to module to use with NodeJS applications to parse, validate, manipulate and format date when building APIs and storing them as data in a preferred database. It is lightweight library and now supports ECMAScript 6. [**moment/moment**](https://github.com/moment/moment) ### gm GraphicsMagick and ImageMagick are two popular tools for creating, editing, composing and converting images. With module `gm` you can use both tools directly from within your JavaScript code in a NodeJS application. The module supports all the typical required to operate on an image: - resizing - clipping - encoding ```js var fs = require('fs'), gm = require('gm').subClass({ imageMagick: true }); // resize and remove EXIF profile data gm('/path/to/my/img.jpg').resize(240, 240); ``` [**aheckmann/gm**](https://github.com/aheckmann/gm) ### sharp With over 11k+ stars on its Github repository, `sharp` is a high performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP and TIFF images. The typical use case for this high speed Node.js module is to convert large images in common formats to smaller, web-friendly images with different dimensions. Resizing an image is typically 4x-5x faster than using the quickest ImageMagick and GraphicsMagick settings. ```js sharp('input.jpg') .rotate() .resize(200) .toBuffer() .then( data => ... ) .catch( err => ... ); ``` [**lovell/sharp**](https://github.com/lovell/sharp) ### node-csv The CSV (comma-separated values) format is often used when interchanging table-based data. For example, Microsoft Excel allows you to export or import your data in that format. `node-csv` simplifies the process of working with CSV data in a server side application. node-sv provides functionalities for generating, parsing, transforming and stringifying CSV and uses streams API for that. It also comes with a callback API, a stream API and a synchronous API to fulfil your needs. [**adaltas/node-csv**](https://github.com/adaltas/node-csv) ### Passport Passport is an ExpressJS compatible authentication middleware for Node.js. Its sole purpose is to authenticate requests which is done through an extensible set of plugins known as strategies. Passport does not mount routes or assume any particular database schema, which maximizes flexibility and allows application-level decisions to be made by the developer. The API is simple and requires you to provide a request to authenticate, and Passport provides hooks for controlling what occurs when authentication succeeds or fails. ```js passport.serializeUser(function (user, done) { done(null, user.id); }); passport.deserializeUser(function (id, done) { User.findById(id, function (err, user) { done(err, user); }); }); ``` [**jaredhanson/passport**](https://github.com/jaredhanson/passport) ### Nodemailer An open source package, nodemailer lets you send emails just by using it inside a NodeJS app. It is a single module with zero dependencies so you can use it freely without worrying much about sensitive data leaking. It also secures email delivery using TLS/STARTTLS and you can attach deliverables with your message. The standard Node.js API does not offer such a feature, but fortunately the module Nodemailer fills this gap. [**nodemailer/nodemailer**](https://github.com/nodemailer/nodemailer) ### ndb `ndb` is an improved debugging experience for Node.js, developed and enabled by the team behind Google's Chrome web browser. Currently, it is recommended to use Node `v10.x.x` but if you are considering using this package to debug your Node apps, you are required a minimum version of `8.x.x`. ndb has some powerful features exclusively for Node.js: - Child processes are detected and attached - You can place breakpoints before the modules are required - You can edit your files within the UI - By default, `ndb` blackboxes all scripts outside current working directory to improve focus. This includes node internal libraries (like `_stream_wrap.js`, `async_hooks.js`, `fs.js`) - supports memory profiler, JS sampling profiler, breakpoint debugging, async stacks and so on [**GoogleChromeLabs/ndb**](https://github.com/GoogleChromeLabs/ndb) ### lodash This is a utility library that provides extra functionalities such as iteration, manipulation of values, testing values, and creating composite functions that work with arrays, objects, numbers, strings and so on. It is one of the most popular open source library in Nodejs ecosystem. ```js // Load the full build. const _ = require('lodash'); // Load the core build. const _ = require('lodash/core'); // Load the FP build for immutable auto-curried iteratee-first data-last methods. const fp = require('lodash/fp'); ``` [**lodash/lodash**](https://github.com/lodash/lodash) ### axios A promise based HTTP client that provide extra features over `fetch` from native JavaScript API, axios is a popular utility tool among both front-end JavaScript developers and NodeJS. It has following features: - Make XMLHttpRequests from the browser - Make http requests from node.js - Supports the Promise API - Intercept request and response - Transform request and response data - Cancel requests - Automatic transforms for JSON data - Client side support for protecting against XSRF Most of the above enlisted features have ae absent from native `fetch` JavaScript API and adds to the advantage of using `axios`. Check out the example below in which `axios` is being used with `async/await` syntax. ```js async function getUser() { try { const response = await axios.get('/user?ID=54321'); console.log(response); } catch (error) { console.error(error); } } ``` ### Socket.io This is a library that enables bi-directional communication in real time by using WebSockets. It provides reliability for handling proxies and load balancers, personal firewalls and antivirus software, and supports binary streaming. Other features include auto-connection support where unless instructed otherwise a disconnected client will try to reconnect forever until the server is available again. Used by organizations such as Microsoft, Zendesk, and Trello it also includes real-time analytics with counters, logs and charts and has a variety of use cases in IoT. ```js io.on('connection', socket => { socket.emit('request' /* … */); // emit an event to the socket io.emit('broadcast' /* … */); // emit an event to all connected sockets socket.on('reply', () => { /* … */ }); // listen to the event }); ``` [**socketio/socket.io**](https://github.com/socketio/socket.io) ### PM2 It is a Production Runtime and Process Manager for Node.js applications with a built-in Load Balancer. It allows you to keep applications alive forever, to reload them without downtime and facilitate common devops tasks. Starting an application is easy as well as managing one too. PM2 has container support as well with the drop-in replacement command for node, called pm2-runtime, run your Node.js application in a hardened production environment. It supports all major Node.js frameworks such as Express, Sails, Hapi and so on. [**Unitech/pm2**](https://github.com/Unitech/pm2) ### Joi Introduced with HapiJS, Joi has become a popular library to validate incoming data requests. If you have ever used an ORM when building your Node application such as Sequelize or Mongoose, you know that it is possible to set validation constraints for your model schemas. This makes it very easy to handle and validate data at the application level before persisting it to the database. When building APIs, the data usually come from HTTP requests to certain endpoints, and the need may soon arise to be able to validate data at the request level. Joi is used to validate schema objects with additional rules provided by its own API. Moreover, it works with any Nodejs framework rather than just HapiJS. ```js const Joi = require('joi'); const schema = Joi.object() .keys({ username: Joi.string().alphanum().min(3).max(30).required(), password: Joi.string().regex(/^[a-zA-Z0-9]{3,30}$/), access_token: [Joi.string(), Joi.number()], birthyear: Joi.number().integer().min(1900).max(2013), email: Joi.string().email({ minDomainAtoms: 2 }) }) .with('username', 'birthyear') .without('password', 'access_token'); ``` [**hapijs/joi**](https://github.com/hapijs/joi) ### TypeORM Whether you want to work with TypeScript enabled Nodejs server or make use of latest ES6, ES7 JavaScript features to create an API for your application, TypeORM is a popular library that work with multiple databases. It supports both Active Record and Data Mapper patterns, unlike all other JavaScript ORMs currently in existence, which means you can write high quality, loosely coupled, scalable, maintainable applications the most productive way. For example, a typical connection to a database using TypeORM looks like: ```js import 'reflect-metadata'; import { createConnection } from 'typeorm'; import { Photo } from './entity/Photo'; createConnection({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'admin', database: 'test', entities: [Photo], synchronize: true, logging: false }) .then(connection => { // here you can start to work with your entities }) .catch(error => console.log(error)); ``` It also supports MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, WebSQL databases. [**typeorm/typeorm**](https://github.com/typeorm/typeorm) ### Sequelize It is a promise based Nodejs ORM that supports multiple SQL based databases such as Postgres, MySQL, MariaDB, SQLite and Microsoft SQL Server. It features solid transaction support, relations, read replication and more. It comes with its own CLI tool that enables data migrations and model/schema creation easy. It has a simple installation process, all you have to do is install `sequelize` in your Nodejs application along with the driver of that database you are using. ```shell $ npm install --save sequelize # And one of the following: $ npm install --save pg pg-hstore $ npm install --save mysql2 $ npm install --save mariadb $ npm install --save sqlite3 $ npm install --save tedious # MSSQL ``` [**sequelize/sequelize**](https://github.com/sequelize/sequelize) ### Mongoose MongoDB is a commonly used NoSQL database in Nodejs applications. It stores the data in JSON documents and the structure of these documents can vary as it is not enforced like SQL databases. Mongoose is an Object Data Modelling (ODM) library for MongoDB and Node.js. It manages relationships between data, provides schema validation, and is used to translate between objects in code and the representation of those objects in MongoDB. ```js const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true }); const Cat = mongoose.model('Cat', { name: String }); const kitty = new Cat({ name: 'Zildjian' }); kitty.save().then(() => console.log('meow')); ``` [**Automattic/mongoose**](https://github.com/Automattic/mongoose) ### MochaJS Mocha.js is a JavaScript test framework based on Node.js. It enables you to test both in console and in the browser. You can use this really fast testing suite to do the unit and integration testing plus it works with testing patterns such as TDD (_Test-Driven Development_) and BDD (_Behavior Driven Development_). Mocha works well with other assertion libraries such as Chai, Sinon, Should.js. This is an advantage and the reason for its popularity. ```js const assert = require('assert'); describe('Array', function () { describe('#indexOf()', function () { it('should return -1 when the value is not present', function () { assert.equal([1, 2, 3].indexOf(4), -1); }); }); }); ``` [**mochajs/mocha**](https://github.com/mochajs/mocha) ### Chai Chai is a TDD and BDD assertion framework for Node.js which can be paired with any test runner framework. As an assertion tool, you can use Chai with its rich plugin system such as `chai-as-promised`, `chai-events`, `chai-spies-next`. It gives me much simpler and more readable tests than using my own assertion helpers or other less popular libraries. [**chaijs/chai**](https://github.com/chaijs/chai) ### SinonJS This is a standalone testing framework for Node.js. The advantage it possess is that it works with any testing framework. You will find many examples of it being used with Mocha and Chai. It requires minimal integration and supports stubs, spies and mocks. It also supports most browsers (cross-browser support) and runs on the server using Node.js. [**sinonjs/sinon**](https://github.com/sinonjs/sinon) ### AVA This is a minimal testing framework to test Node.js applications. It utilizes the async I/O nature of Node and runs concurrent tests, hence, it vastly decreases test suite times. Some of its highlights are: - Magic assertion in which it adds code excerpts and clean diffs for actual and expected values. If values in the assertion are objects or arrays, only a diff is displayed, to remove the noise and focus on the problem. - Clean stack traces by automatically removing unrelated lines in stack traces, allowing you to find the source of an error much faster, as seen above. - supports latest JavaScript features using [**Babel 7**](https://babeljs.io/). [**avajs/ava**](https://github.com/avajs/ava) ### Jest Jest is an open source framework that built for writing and running tests in JavaScript. It is open source, created and maintained by the Facebook. It is built with multiple layers on top of jasmine (_another test running framework_) by keeping some of good parts from jasmine. Its strengths are: - is fast - it can perform snapshot testing - is opinionated, and provides everything out of the box without requiring you to make choices The advantage it has over other NodeJS testing frameworks such as Mocha that it uses its own assertion API whereas using Mocha you have to install another third party module in order to create and run tests. Jest is human friendly framework. It has gained its attraction by its well supported and very fast testing behavior. [**facebook/jest**](https://github.com/facebook/jest) ### CloudRail Using CloudRail, you can easily integrate external APIs into your application. CloudRail provides abstracted interfaces that take several services and then exposes a developer-friendly API that uses common functions between all providers. This means that, for example, upload() works in exactly the same way for Dropbox as it does for Google Drive, OneDrive, and other Cloud Storage Services, and getEmail() works similarly the same way across all social networks. [**CloudRail/cloudrail-si-node-sdk**](https://github.com/CloudRail/cloudrail-si-node-sdk) ### agenda Job scheduling is a big part of any server side framework. Luckily, Nodejs has one awesome framework to schedule jobs to run at a particular time and run on a particular day. `agenda` is a light-weight job scheduling library for Node.js. It uses promised based API and has Mongo backed persistence layer. [**agenda/agenda**](https://github.com/agenda/agenda) ### Nodemon It is a tool that helps develop Node.js based applications by automatically restarting the node application when file changes in the directory are detected. It does not require any additional changes to your code or method of development. `nodemon` is a replacement wrapper for node, to use `nodemon` replace the word `node` on the command line when executing your script. It was originally written to restart hanging processes such as web servers, but now supports apps that cleanly exit. [**remy/nodemon**](https://github.com/remy/nodemon) ### Keystone CMS It is a content management system and web application framework built on Express framework and uses Mongoose as the ODM. It makes it easy to create sophisticated web sites and apps, and comes with a beautiful auto-generated Admin UI. Currently, the Admin UI is a single page application written using React, Redux and Elemental UI. You can use your own Express instance and integrate Keystone as a library. [**keystonejs/keystone**](https://github.com/keystonejs/keystone) ### Strapi Another open source Content Management System for Nodejs application, Strapi has its own advantages. One of them is it being headless and supporting multiple databases such as MySQL, Postgres and MongoDB. It has many features such as: - Modern Admin Panel: Elegant, entirely customizable and fully extensible admin panel - Secure by default: Reusable policies, CSRF, CORS, P3P, Xframe, XSS - Plugins Oriented: Install auth system, content management, custom plugins, and more, in seconds - Powerful CLI: Scaffold projects and APIs on the fly - Front-end Agnostic: Use any front-end frameworks (React, Vue, Angular, and more.), mobile apps or even IoT - Blazing Fast: Built on top of Node.js, Strapi delivers amazing performances. [**strapi/strapi**](https://github.com/strapi/strapi) ### FakerJS When we start to build an application, we generally do not want to worry much about data. To create a database and fill it with sample data seems much of a hassle to me personally. ```js var faker = require('faker'); var randomName = faker.name.findName(); // Rowan Nikolaus var randomEmail = faker.internet.email(); // Kassandra.Haley@erich.biz var randomCard = faker.helpers.createCard(); // random contact card containing many properties ``` FakerJS a wonderful node module to create fake/mock data when you are starting to build a prototype or an application. It has its own API that has a variety of generators to construct mock data as per your needs. [**Marak/faker.js**](https://github.com/marak/Faker.js/) ### Dotenv Saving sensitive data in the form of environmental variables is one good practice to be followed when working with Nodejs web frameworks. Environmental variables are local variables that are made available to an application. Creating these variables is made easy with a tool like `dotenv`. This module loads environment variables from a `.env` file that you create and adds them to the `process.env` object that is made available to the application. This module allows you to create secret keys that your application needs to function and keep them from going public. [**motdotla/dotenv**](https://github.com/motdotla/dotenv) ### Conclusion Nodejs is a mature platform. Working with third party libraries is a huge part of the JavaScript ecosystem and you cannot run from it. Apart from your personal opinion, if you do not appreciate working with different third party libraries then you should definitely think about the tech stack you are working with. _I hope this list gets you started to with most commonly used open source packages that are used in_ [**_Node.js_**](https://nodejs.org) _community._ --- ## 3 Steps to learn React Native in 2019 Slug: 3-steps-to-learn-react-native-in-2019 ![cover_image](https://i.imgur.com/LUgTT2v.jpg) If you are interested in writing code for mobile applications using JavaScript, you are reading the right post. React Native, developed and maintained by Facebook, is an open-source framework to develop cross-platform mobile applications, using the programming language JavaScript. Currently, in its **0.57 version,** React Native is based on Facebook’s front-end library called ReactJS and shares many concepts. If you are familiar with React, kudos to you! You have crossed the first hurdle. Coming from a frontend development background, React uses a virtual DOM which acts as a shadow to the real DOM available. When an element changes, that change is reflected on the real DOM by Virtual DOM using a node that corresponds to each element. However, in React Native, there is no DOM other than Native Components which are provided by platforms such as iOS and Android. There are no web views here. React Native has an instance of [**JavaScriptCore**](https://facebook.github.io/react-native/docs/javascript-environment.html) to execute JS code when an application starts. React Native uses RCTBridgeModule to make a connection between native code and JavaScript code. It is currently being used by Facebook, Instagram, Uber, Wix, Tesla and many more. Here is what I think you can do to advance with React Native development. ## Start with basics This article briefly provides you with an overview of what is inside the React Native app development process and how things work behind the scenes, briefly. I often come across (especially through [_#100DaysOfCode_](https://x.com/_100DaysOfCOde) campaign) developers who struggle to learn a new framework with little to no background in specific the programming language. My advice, is before you leap to make gigantic projects, start with the basics. Learn the concepts as each specific component to the curve, make sure to apply them as much as you can and build small things. For example, learn how to use `FlatList` component. Try creating a list with your own dataset or find a mock/fake data set on the internet and try to build a small app out of it. Always remember the feeling you got from creating your first _Hello World_ program. Do you remember that sense of accomplishment? Take small steps, and build small things at first before dipping your toes deep in the complexity of state management libraries such as Redux and Mobx, or persisting data, using third-party APIs, using TypeScript or Flow, and so on. These are just tools, you do not need to know them on day one (_but I am not saying you have to never learn about them. The keyword here is that they are TOOLS_). If you are new to JavaScript, make sure you are clear with the basic ES6 features such as classes, arrow functions, and so on. Then, you must go through the basic ReactJS concepts such as props, state, and stateless components in general. In summary, start by familiarizing yourself with: - ES6 Features - ReactJS Components API and LifeCycle methods - Setting up a development environment for React Native - Flexbox ## Advance your way Once you have basic concepts clear in your mind and have played around a bit to get some amount of hands-on experience, it is time to advance further. Start building bigger apps that work or behave like real applications and interact with real-time data. Here is a list of things you can learn to advance in your journey. - Offline data storage with `AsyncStorage` - Working with third-party APIs - Maps - Splash Screens - Navigation - Redux (for state management) - Redux Saga and Persist - Tests and TDD - Push notifications - UI Animations - Build and publish your app - Continuous Delivery or CI Note that these are just broad topics to get you started. There are many other things you will learn along the way. Don’t get overwhelmed by that. ## Personal Challenges: What do you want out of it? Maybe you to become professional a React Native developer and work in an organization that uses this tech framework or maybe you want to build apps for your clients/customers. Setting your challenges in the way is a great way to accomplish things and learn. Commit yourself and work on it. Find apps on your phone or in stores that you want to clone or add an extra feature as a functionality, or learn about the user interface. Do not get overwhelmed by the number of mistakes you make or the errors you get. Getting frustrated and ranting/complaining about it over the internet all day is easy but understand this is that, it will not solve your problems or make you a better developer. All of this is a part of your journey. Keep reminding yourself of that. ## Conclusion In simple words, React Native brings React to mobile app development. Its goal isn’t to write the code once and run it on any platform. The main goal here is to learn once and write anywhere. An important distinction to make. [Originally published at The Startup](https://medium.com/swlh/3-steps-to-learn-react-native-in-2019-5cdb3d1e1c84) --- ## Accessing Geo-location and App Permissions in React Native and Expo Slug: accessing-geo-location-and-app-permissions-in-react-native-and-expo > [Originally published at React Native Training](https://medium.com/react-native-training/accessing-geo-location-and-app-permissions-in-react-native-and-expo-e7a1bd4714a2) ![cover_image](https://i.imgur.com/jEjHKCI.jpg) In web, Geolocation is provided as an API that has different methods to use in a web application. Similarly, React Native takes advantage of this API and is available as polyfills. Geolocation is a must have feature to implement in a mobile app. Few of the famous mobile apps that use it for more than 90% in terms of usage are Uber, Google Maps, and more. In this article, I will show how to integrate the Geolocation API in a React Native app in two ways. Using Expo and using `react-native-cli`. Along with that, I am going to implement a real time feature that is commonly used with these types of applications. Asking **user permissions**. Permissions in `react-native-cli` can be a bit tricky but after reading this article, it won't be tricky to you as much. ## Getting Started With Expo For this purpose, I am using `expo-cli`. Follow the below commands to set up an Expo project and get started. ```shell npm install -g expo-cli expo-cli init find-me # select blank template & traverse into a newly created directory npm run ios # for Window users, run npm run android ``` You will be welcomed with a default screen. We will start here. First, edit the `App.js`. ![ss](https://i.imgur.com/kWvPIWF.png) Create a new file for the `FindMe` component at `src -> screens -> FindMe -> index.js`. Inside this file, we will just display a text. ![ss](https://i.imgur.com/rRRzQVb.png) Here is how our app looks so far. ![ss](https://i.imgur.com/9oCTyax.png) ## Accessing Geolocation API The Geolocation API exists as a global object called `navigator` object in React Native, just like the web. It is accessible via `navigator.geolocation` in our source code and there is no need to import it. For our demonstration purposes, we will be using `getCurrentPosition` method from the geolocation API. This method allows a mobile app to request a user's location and accepts three parameters: success callback, error callback and a configuration object in the last. ![ss](https://i.imgur.com/oeYTXrk.png) The first callback has a `position` argument that is an object with the following properties. ```shell { "timestamp": 1533729980953.91 "coords": { "accuracy": 5, "altitude": 0, "altitudeAccuracy": -1, "heading": -1, "latitude": 37.785834, "longitude": -122.406417, "speed": -1 } } ``` Now, implement this in our `FindMe` component. ![ss](https://i.imgur.com/totMaU5.png) We start be importing `TouchableOpcaity`. It is a wrapper that responds accurately to user touches. In a mobile app, you will be making use of them. Think of it as a button in a web application. This newly imported wrapper accepts an `onPress` prop that is going to trigger the function defined as in the value, in our case `findCurrentLocation`. `findCurrentLocation` holds the logic of fetching a user's location. We are also using the local state to display coordinates from the data provided to us by `position` object. The text `Where Am I` now becomes clickable. ![ss](https://i.imgur.com/Wj58GJo.png) That’s it for the app part. Now let us see how to add permissions to the same application. ## Using Expo Permissions Requesting to access a user’s information whether it is location or any other sensitive information on the device, it is your job as the developer to ask for the permissions first. It is one time process, both when developing the application and when the user is using the application. Most permissions are asked when the user installs the application run it for the first time. For us, Expo has integrated all the permission API we need for this demo app or any other app you are building using Expo. This API has different methods for device types of permissions to grant for. Such as location, camera, audio recording, contacts, camera roll, calendar, reminders (for ios only) and notifications. We are going to use `Location`. ![ss](https://i.imgur.com/e4InNct.png) We change our state a bit. It will not store the whole geolocation object and `errorMessage` in case of an error. Our `findCurrentLocation` remains the same. In fact, we are not using it. Expo has a method for us that does the same. It is called `getCurrentPositionAsync`. It will only fetch the user's location and other properties made available by `getCurrentPosition` method from Geolocation API and if the permission is granted. In the render method, `onPress` prop is now calling a different method `findCurrentLocationAsync` that holds the logic for asking permission and fetches the location data after the user has granted permission to our app to access it. If not, the error message is set otherwise the location in the state is updated. The last step is for android users. Open `app.json` and permissions. ![ss](https://i.imgur.com/NvFVYyw.png) ![ss](https://i.imgur.com/gov0n1z.png) If you press allow, you will see the following result. ![ss](https://i.imgur.com/hEh9YBC.png) Note that in even in development mode and running the app in a simulator, the permissions are only asked once. To perform this again, you will have to delete the app from your simulator and re-run the command to start the expo app. ## Using react-native-cli Using `react-native-cli` means you will have to set permissions on your own, however, the logic of getting a user's location is going to be the same. There are no templates in `react-native-cli` so once the directory is generated, traverse into it and run `npm start` to see if everything is installed correctly. The first thing you will notice when you open this project in an IDE or a code editor is that there is a vast amount of change in the structure of files and folders. Expo had a sort of minimal project structure as compared to this one. There are separate build folders such as `/android` and `/ios` for each platform. You can also use flow (which is similar to TypeScript, open sourced by Facebook). ![ss](https://i.imgur.com/MOyLOFw.png) We will only modify `App.js` file with the following code. ![ss](https://i.imgur.com/CPryJLE.png) Observe that `findCoordinates` works the same way as in Expo application and also the code in `render()` function is exactly same. Our next step is to set permissions. In ios, geolocation is enabled by default when a project is created using `react-native-cli`. To use it, we just need to include a key in `info.plist` which is inside the `ios/findCoordsApp` directory. ![ss](https://i.imgur.com/Iqc2Y2c.png) For android, we need to add the following line in `android/app/src/AndroidManifest.xml` file. ```xml ``` ![ss](https://i.imgur.com/eqkj6TT.png) Now if you run your application you will see the following screen. ![ss](https://i.imgur.com/528AV75.png) Click on the text and you will be prompted to ask whether to allow the application to request for user’s location or not. For the demonstration purpose, I am using an android Emulator since we have already seen how it works on ios Simulator in the Expo section. ![ss](https://i.imgur.com/RVPukGJ.png) If you press allow, you will see the following result. ![ss](https://i.imgur.com/z7MQSko.png) _You can find the complete code in this Github repository._ 👇 [**amandeepmittal/findCoordsApp**](https://github.com/amandeepmittal/findCoordsApp) If you want to learn more about working with Geolocation API in a React Native application, please go through the [**official documentation**](https://facebook.github.io/react-native/docs/geolocation)**.** [**Expo’s Documentation**](https://docs.expo.io/versions/latest/sdk/permissions#__next) has a lot more on **Permissions** too. --- ## Adding Bluesky icon to my Astro blog Slug: add-bluesky-icon-to-astropaper I use [AstroPaper](https://github.com/satnaing/astro-paper) as the default theme for my blog. It is a minimal, responsive and SEO-friendly Astro blog theme. Astro is a modern static site builder that allows you to build faster websites with less client-side JavaScript. This theme is minimal and customization-friendly. With [Bluesky growing rapidly, adding over one million users daily](https://www.cnet.com/tech/bluesky-explained-why-this-social-media-network-is-now-growing-by-1-million-users-daily-luke-skywalker/), is built using Expo (where I currently work), and attracting the "tech Twitter" community (yes, Twitter, not X), it reminds me of Twitter's early days. I decided to look into [AstroPaper's GitHub repository](https://github.com/satnaing/astro-paper) to see if there are any new updates for adding this new icon. The instructions in this post are from [this closed PR](https://github.com/satnaing/astro-paper/pull/209). --- Lucky for us, all the significant social icons are defined and stored in one file called `src/assets/socialIcons.ts`: ```ts const socialIcons = { // All social SVG icons... }; ``` Let's update the `socialIcons` object by adding a new SVG icon called `Bluesky`, with the following code: ```ts const socialIcons = { // Other social svgs... Bluesky: ` ` }; ``` In the AstroPaper theme, the `SOCIALS` array inside the `stc/config.ts` controls the active social profiles displayed on the blog. Let's add the `Bluesky` social link to this array so that the link will appear on its own. ```ts export const SOCIALS: SocialObjects = [ // Other social links... { name: 'Bluesky', href: 'https://bsky.app/profile/aman.bsky.social', linkTitle: `${SITE.title} on Bluesky`, active: true } ]; ``` Here's what's happening in the snippet above: - **`name`**: The name of the social media platform. - **`href`**: Your Bluesky profile URL. - **`linkTitle`**: Tooltip text shown on hover. - **`active`**: Displays the icon when set to `true`. After making this change, the icon will appear (depending on how you customized the AstroPaper theme for your blog) in the hero section and the footer: ![Bluesky icon in hero section](/images/bluesky-icon/ss2.png) ![Bluesky icon in footer](/images/bluesky-icon/ss1.png) This is great, and you are done! However, I wouldn't say I liked how the icon is filled with the current color. To match it with the rest of the icons and make it appear outline, set the value of `fill` to `none` and add the `stroke-width` to `4` inside the `src/assets/socailIcons.ts` file: ```ts const socialIcons = { // Other social svgs... Bluesky: ` ` }; ``` You can always change the stroke width (`stroke-width`) as you like. Now, the updated icon will match the other social icons: ![Updated Bluesky icon in hero section](/images/bluesky-icon/ss3.png) Following the instructions above, you can add whichever icon you'd like. I personally find AstroPaper's customization options to keep my blog up to date with minimal effort. What customizations have you tried? Let me know on [Bluesky](https://bsky.app/profile/aman.bsky.social)! --- ## Add Environment Variables in a Netlify Deployment Slug: add-environment-variables-in-netlify-deployment ![cover_image](https://i.imgur.com/SX2uERE.png) Recently I migrated my blog (the one you reading right now) from [Gatsby](https://www.gatsbyjs.com/) to [Next.js](https://nextjs.org/) for some specific reasons. The first reason, me being curious about how Next.js works and how is the developer experience. The second one, I want to have minimal effort and spend less time in managing a blog's UI and spend more time on writing posts. Lately, the vice versa has been the reality. To not write every CSS element from scratch for responsive design, I am using [Chakra UI](https://blog.logrocket.com/how-to-create-forms-with-chakra-ui-in-react-apps/) for this blog which is a UI component library for React apps. It serves the purpose. ## The idea for the post The idea of the post came from a problem of my own. Since I am a fan of minimal effort, this blog has been running from a [GitHub repository](https://github.com/amandeepmittal/amanhimself.dev) deployed with [Netlify](https://www.netlify.com/). There are many advantages to this bare infrastructure such as free HTTPS certificate, using a custom domain, and so on. This way, Netlify manages continuous deployment runs the build command from a Gatsby or a Next.js rendered site. It also triggers a deployment whenever there is a new commit pushed in the GitHub repository. ## The problem that leads me to use an environment variable That said, let's get back to the main topic. After deploying the blog with 90+ posts, I ran into an issue that is known as "Allocation failed — JavaScript heap out of memory" in Node.js world. The issue occurs when the deployment build runs out of memory. Node.js does not handle this by rolling back and moves forward with the build which eventually concludes in the build to fail. Regardless of what stack or framework you use to deploy your site, if the deployment instance is using a node server, one day you might run into an issue as shown below. ![ss1](https://i.imgur.com/heymZ2D.png) ## The good "Old space" problem in V8 Diving further I got to learn a new thing even though I've been using Node.js since the starting of my own developer career. This issue occurs when the memory consumption of garbage collection in V8 reaches its max limit. If the limit is exceeded, V8 kills the process. > More information on this issue can be found on [Stackoverflow here](https://stackoverflow.com/questions/48387040/nodejs-recommended-max-old-space-size/48392705). It also explains, when to increase the memory and when to avoid. ## Increasing the memory limit with Environment Variable According to Chris McCraw's answer [here](https://community.netlify.com/t/fatal-error-call-and-retry-last-allocation-failed-javascript-heap-out-of-memory/1840/4), a Netlify build (if not using an Enterprise plan) should not exceed the limit of 3GB for reliability. In the same thread, there is a proper solution and that leads to the use of environment variables. If you face this problem using the same approach with Netlify as I did, or for some other reason you want to use environment variables, here is how to get started. Step one is to go to your deployment on Netlify and click the option **Deploy Settings**. ![ss2](https://i.imgur.com/qjEP2pR.png) This is where all the deployment-related settings are stored. On the next screen, observe a sidebar and under the tab **Build & deploy** there is a sub-tab called **Environment**. Click on that and then click on the button **Edit variables**. Now, you can add the key and value for each environment variable as shown below. ![ss3](https://i.imgur.com/5NKWri1.png) ## Further reading Here is a list of all the links I came across when resolving this issue: - [The JavaScript heap out of memory thread](https://community.netlify.com/t/fatal-error-call-and-retry-last-allocation-failed-javascript-heap-out-of-memory/1840/4) - [The Stackoverflow thread that explains shortcomings of V8 in managing memory](https://stackoverflow.com/questions/48387040/nodejs-recommended-max-old-space-size/48392705) - [Features of Netlify + GitHub app](https://github.com/apps/netlify) --- ## How to add opacity to a Pressable component in React Native Slug: add-opacity-to-pressable-component-react-native `Pressable` component was introduced in React Native in 2020 as a core component wrapper. It can be used instead of existing touchable components including `TouchableOpacity`, `TouchableHighlight`, and `TouchableWithoutFeedback`. These components include styles and effects that sometimes do not meet the desired outcome on individual platforms (Android and iOS). The way I see using the `Pressable` component is that it can be customized in terms of style, appearance, and extended functionality by creating a custom wrapper/component around it. It also offers a lot of props such as `onPressIn`, `onPressLong`, `hitSlop`, `delayLongPress` and so on, that can be used to implement these extended functionalities. At times, one thing I like to do is to add opacity feedback when the touch is active. It doesn't provide in the form of a prop directly. Something similar to what [activeOpacity](https://reactnative.dev/docs/touchableopacity#activeopacity) prop on TouchableOpacity does. In this post, let's build a wrapper component that uses `Pressable` to add opacity feedback to the component. ## Creating a wrapper component Start by creating a custom `Pressable` component with no styles of its own so it can be a reusable component. ```tsx // Pressable.tsx import { useCallback } from 'react'; import { Pressable as RNPressable, PressableProps, PressableStateCallbackType } from 'react-native'; type CustomPressableProps = PressableProps & {}; export default function Pressable({ children, style, ...props }: CustomPressableProps) { const customStyle = useCallback( (state: PressableStateCallbackType) => { if (typeof style === 'function') { return style(state); } return style; }, [style] ); return ( {children} ); } ``` So far, it accepts only three props: - `children` that is used to add a label on the button (using like a `Text` component from React Native) - `style` that is used to add styles to the button - `...props` is used to pass down all the props to the underlying `Pressable` component. The `useCallback` hook is used to memoize the `customStyle` function. This is to ensure that the `customStyle` function is not recreated on every render. Since React Native's `Pressable` component accepts style as either an object or a function, you need to pass the pressed state in case it is a function. Otherwise, it will throw a TypeScript error. If the `style` is an object, it can be returned as is. In the above code, since the wrapper component you are creating will only be responsible for handling opacity, other important props like `onPress` are left to be handled where this wrapper component will be used. ## Using the wrapper component To use the wrapper component in its current state, import it: ```tsx // app/(tabs)/index.tsx import { View, Text } from 'react-native'; import CustomPressable from '@/components/Pressable'; export default function Home() { return ( Press Me ); } ``` Make sure to add some styles to the `Text` and the `Pressable` components. ## Running the example Here is the output I get by running the code above. Notice that there is no visual feedback when I press the component on the app screen. ![ss1](https://i.imgur.com/XcLXQbn.gif) I used the [Touch indicator on an iOS simulator](https://amanhimself.dev/blog/show-touch-indicator-on-ios-simulator/) to show that the button is pressed. ## Adding opacity prop to the wrapper component In some scenarios, you may want to add and use opacity as the feedback. For example, decrease the opacity to `0.5` when the button is being pressed. You can extend the `styles` to accept a `pressed` state. It is a boolean that tells whether the component is currently pressed or not. Using it, you can alter the value of the opacity property in styles. In the wrapper component, add a new prop called `activeOpacity`. This prop accepts a number between `0` and `0.99`. It is used conditionally on the `opacity` property and will only be true when the component is pressed. When the component is not in a pressed state, the opacity value is `1`. ```tsx // Pressable.tsx import { useCallback } from 'react'; import { Pressable as RNPressable, PressableProps, PressableStateCallbackType } from 'react-native'; type CustomPressableProps = PressableProps & { activeOpacity?: number; }; export default function Pressable({ children, style, activeOpacity, ...props }: CustomPressableProps) { const customStyle = useCallback( (state: PressableStateCallbackType) => { const { pressed } = state; const baseStyle = { opacity: pressed ? activeOpacity : 1 }; if (typeof style === 'function') { const derivedStyle = style(state); return [baseStyle, derivedStyle]; } return [baseStyle, style]; }, [activeOpacity, style] ); return ( {children} ); } ``` ## Running the example with activeOpacity value The below code snippet modifies the previous example to add an activeOpacity value of `0.5`: ```tsx export default function Home() { return ( Press Me ); } ``` The output after this step confirms that the opacity is changing as expected. ![ss2](https://i.imgur.com/LDyiXIu.gif) ## Conclusion The [`Pressable` component](https://reactnative.dev/docs/pressable) has many props that can be used to write an extensive and customized wrapper that fulfills your app's requirements. It is preferred in the official React Native documentation and provides a future-proof way of handling touch-based events. --- ## How to add a Search bar in a FlatList in React Native apps Slug: add-search-bar-to-a-flatlist-in-react-native ![cover_image](https://i.imgur.com/zeVnUHd.png) > Originally published at [Crowdbotics.com](https://blog.crowdbotics.com/add-search-bar-flatlist-react-native-apps) There are few ways to create scrollable lists in React Native. Two of the common ways available in React Native core are `ScrollView` and `FlatList` components. Each has its strength and in this tutorial, let us dive deep to create a search bar with `FlatList` component. The final result you are going to achieve at the end of this tutorial is shown below. ![ss8](https://i.imgur.com/a5FpiUm.gif) ## Table of contents - Getting started - What is FlatList? - Basic usage of a FlatList component - Fetching data from Remote API in a FlatList - Adding a custom Separator to FlatList component - Adding a Search bar - Run the app - Add clear button to input text field - Conclusion ## Getting started For the demo we are going to create in this tutorial, I am going to use [Expo](https://expo.io/). You are free to choose and use anything between an Expo CLI or a `react-native-cli`. To start, let us generate a React Native app using Expo CLI and then install the required dependency to have a charming UI for the app. Open up a terminal window and run the following commands in the order they are mentioned. ```shell expo init searchbarFlatList cd searchbarFlatList yarn install @ui-kitten/components @eva-design/eva lodash.filter expo install react-native-svg ``` _Note_: The dependency `react-native-svg` is required as a peer dependency for the UI kitten library. UI Kitten is ready to use now. To check, everything has installed correctly, let us modify `App.js` file as the following snippet: ```js import React from 'react'; import { ApplicationProvider, Layout, Text } from '@ui-kitten/components'; import { mapping, light as lightTheme } from '@eva-design/eva'; const HomeScreen = () => ( HOME ); const App = () => ( ); export default App; ``` The `ApplicationProvider` accepts two props, `mapping` and `theme`. To run this demo, open up the terminal window and execute the following command. ```shell expo start ``` I am using an iOS simulator for the demo. Here is the output of the above code snippet. ![ss1](https://i.imgur.com/TZ173Uv.png) ## What is FlatList? The component `FlatList` is an efficient way to create scrolling data lists in a React Native app. It has a simple API to work with and is more efficient and preferment with a large amount of information to display in comparison to its alternate. By default, you can just pass in an array of data and this component will do its work. You do not have to take care of formatting the data too often. ## Basic usage of a FlatList component There are three primary props that a FlatList component requires to display a list of data: - `data`: an array of data that is used to create a list. Generally, this array is built of multiple objects. - `renderItem`: is a function that takes an individual element from the `data` array and renders it on the UI. - `keyExtractor`: it tells the list of data to use the unique identifiers or `id` for an individual element. To get understand this pragmatically, let us build a mock an array of data and using `FlatList`, let us display it on our demo app. To start, import the following statements in `App.js` file. ```js import React from 'react'; import { FlatList, View, Text } from 'react-native'; ``` Then, create an array of mock data. ```js const mockData = [ { id: '1', text: 'Expo 💙' }, { id: '2', text: 'is' }, { id: '3', text: 'Awesome!' } ]; ``` Now, modify the `HomeScreen` component with the following snippet: ```js const HomeScreen = () => ( item.id} renderItem={({ item }) => ( {item.id} - {item.text} )} /> ); ``` If the Expo cli command to run the development server is still running, you are going to get the following result. ![ss2](https://i.imgur.com/penUpT7.png) ## Fetching data from Remote API in a FlatList You can even play around with it. Try to fetch data from a real-time remote API and display them in the list instead of mock data. For a start, you can use a public API URL such as [Randomuser.me API](https://randomuser.me/api/). The result to obtain at the end of this section is displayed below. ![ss3](https://i.imgur.com/cAwKEaD.png) Open, `App.js` file and a state object with some properties to keep track of data from the Random User API. Also, do not forget to modify the import statements. ```js // modify the import statements as below import React from 'react'; import { FlatList, View, ActivityIndicator, TouchableOpacity } from 'react-native'; import { ApplicationProvider, Text, Avatar } from '@ui-kitten/components'; import { mapping, light as lightTheme } from '@eva-design/eva'; // add a state object to the HomeScreen component class HomeScreen extends React.Component { state = { loading: false, data: [], page: 1, seed: 1, error: null }; // ... rest of the code } ``` With the HTTP request to the API URL, let us fetch the first 20 results for now. Create a handler method called `makeRemoteRequest` that uses JavaScript's `fetch(url)` where `url` is the API request. It will fetch the results in JSON format. In case of a successful response from the API, the loading indicator (_which is going to add later_) will be false. Also, using the lifecycle method `componentDidMount`, you can render the list of random users at the initial render of the `HomeScreen` component. ```js componentDidMount() { this.makeRemoteRequest() } makeRemoteRequest = () => { const { page, seed } = this.state const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20` this.setState({ loading: true }) fetch(url) .then(res => res.json()) .then(res => { this.setState({ data: page === 1 ? res.results : [...this.state.data, ...res.results], error: res.error || null, loading: false }) }) .catch(error => { this.setState({ error, loading: false }) }) } ``` Next, add a `renderFooter` handler method that is going to display a loading indicator based on the value from the state object. This indicator is shown when the list of data in still being fetched. When the value of `this.state.loading` is true, using the `ActivityIndicator` from react-native components, a loading indicator on the UI screen is shown. ```js renderFooter = () => { if (!this.state.loading) return null; return ( ); }; ``` Here is the output you are going to get when the loading indicator is shown. ![ss5](https://i.imgur.com/FYDMKsp.png) ## Adding a custom Separator to FlatList component Previously, you learned about the three most important props in the FlatList component. It is so flexible that it comes with extra props to render different components to make UI as pleasing to the user. One such prop is called `ItemSeparatorComponent`. You can add your own styling with custom JSX. To do so, add another handler method called `renderSeparator`. It consists of rendering a `View` with some styling. ```js renderSeparator = () => { return ( ); }; ``` This completes all of the handler method currently required. Now, let us replace the previous `FlatList` component in `App.js` file with the following snippet. A list of user names is going to be rendered with an individual item as the user. When pressed it shows an alert message for now but in real-time app, it will go on to display the complete user profile or user's contact. The individual items in the list are going to be separated by the `renderSeparator` method as well as each item is going to display a user image which is composed of `Avatar` component from `react-native-ui-kitten`. The data is coming from the state object. ```js ( alert('Item pressed!')}> {`${item.name.first} ${item.name.last}`} )} keyExtractor={item => item.email} ItemSeparatorComponent={this.renderSeparator} ListFooterComponent={this.renderFooter} /> ``` From the above snippet, you can also notice that the loading indicator handler method `renderFooter()` is also used as the value of a prop called `ListFooterComponent`. You can also use this prop to render other information at the bottom of all the items in the list. One example is to fetch more items in the list and show the loading indicator when the request is made. Here is the output so far. ![ss4](https://i.imgur.com/eszTsxG.gif) ## Adding a Search bar To create a search bar on top of the FlatList, you need a component that scrolls away when the list is scrolled. One possible solution is to create a custom Search bar component and render it as the value of `ListHeaderComponent` prop in a FlatList. Open `App.js` file and add the following prop to the list. ```js ``` The search bar component is going to be an input field that can take the user's name from the end-user. To build one, let us start by modifying the import statements as below. ```js import filter from 'lodash.filter'; import { ApplicationProvider, Text, Avatar, Input } from '@ui-kitten/components'; ``` Next, modify the `state` object and the following variables to it. The `query` is going to hold the search term when the input is provided. The `fullData` is a temporary array that a handler method is going to filter the user's name on the basis of a query. ```js state = { // add the following query: '', fullData: [] }; ``` Since you are already storing the `results` fetched from the remote API, state variable `data`, let us do the same for `fullData` as well. Add the following inside the handler method `makeRemoteRequest()`. ```js makeRemoteRequest = () => { const { page, seed } = this.state; const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`; this.setState({ loading: true }); fetch(url) .then(res => res.json()) .then(res => { this.setState({ data: page === 1 ? res.results : [...this.state.data, ...res.results], error: res.error || null, loading: false, // ---- ADD THIS ---- fullData: res.results }); }) .catch(error => { this.setState({ error, loading: false }); }); }; ``` Next, add the handler method that is going to handle the search bar. By default, it is going to format the search term provided as a query to lowercase. The user's name is filtered from the state variable `fullData` while the state variable `data` stores the final results after the search to render the correct user. ```js handleSearch = text => { const formattedQuery = text.toLowerCase(); const data = filter(this.state.fullData, user => { return this.contains(user, formattedQuery); }); this.setState({ data, query: text }); }; ``` The `contains` handler method is going to look for the query. It accepts two parameters, the first and last name of the user and the formatted query to lowercase from `handleSearch()`. ```js contains = ({ name, email }, query) => { const { first, last } = name; if (first.includes(query) || last.includes(query) || email.includes(query)) { return true; } return false; }; ``` Lastly, add `renderHeader` to render the search bar on the UI. ```js renderHeader = () => ( ); ``` That's it to add a search bar to the FlatList component. ## Run the app To run the app, make sure the `expo start` command is running. Next, go to Expo client and you are going to be prompted by the following screen: ![ss6](https://i.imgur.com/nUcoa0G.png) Next, try to add a user name from the list being rendered. ![ss7](https://i.imgur.com/KCL5zYz.gif) ## Add clear button to input text field The last thing I want to emphasize is that using a custom UI component from a UI library such as UI Kitten, you can use general `TextInputProps` from React Native core as well. A few examples are props such as `autoCapitalize`, and `autoCorrect`. Let us add another prop called `clearButtonMode` that allows the input field to have a clear button appear on the right side. Add the prop to the `Input` inside `renderHeader()`. ```js ``` Now go back to the Expo client and see it in action ![ss8](https://i.imgur.com/fQvxr4j.gif) ## Conclusion This brings an end to this current tutorial. The screen implemented in this demo is from one of the templates from **Crowdbotics' react-native collection**. We use UI Kitten for our latest template libraries. Find more about how to create custom screens like this from our open source project [here](https://github.com/crowdbotics/blueprint-react-native-contacts-screen). You can also find the source code from this tutorial at this [Github repo](https://github.com/amandeepmittal/searchableFlatListDemo). --- ## Advanced code blocks with language labels and copy buttons in Astro Slug: advanced-code-blocks-with-shiki-and-astro Code blocks are the backbone of any technical blog. While basic syntax highlighting serves its purpose, readers expect more polished experiences. These experiences can be provided with small enhancements. Recently, I started enhancing code blocks on my Astro blog to include language labels with a clean header bar on top of each code block. Before this change, this is how a code block would render on my blog: ## Current state of a code block My blog uses Astro as its blog engine, which uses Shiki for syntax highlighting with [dual theme support](/blog/dual-shiki-themes-with-astro/). The foundational configuration is provided by the astro.config.ts file, which I’ll be going to use. Shiki automatically adds a data-language attribute to a code block. This is the key to extracting language information for the header labels. Let’s take the following code block as an example. The above code block is rendered and has a `ts` language set in its markdown source file. Inspecting developer tools in a browser, the same `ts` language is passed down as a value to the `data-language` attribute. Since Shiki does this heavy lifting, all that is needed is to render this information in a blog post without affecting the business logic behind it. ## Initial JavaScript implementation Using client-side JavaScript, you can easily detect code blocks and programmatically add headers. I have a layout file called `PostDetails. Astro`, which already had a copy button functionality, so it took a lot less guesswork as to where the rendering of a code block's language should go. ```js function attachCopyButtons() { let copyButtonLabel = 'Copy'; let codeBlocks = Array.from(document.querySelectorAll('pre')); for (let codeBlock of codeBlocks) { let wrapper = document.createElement('div'); wrapper.style.position = 'relative'; // ... rest of the function } } attachCopyButtons(); ``` This function creates a wrapper element for each code block. It begins by defining the button label text and querying for all `
` elements, which Shiki uses to wrap code blocks. It then converts a list of notes into an array with `Array.from()` to provide access to array manipulation. For each code block, a div is created with appropriate CSS classes.

## Language formatting

Let’s add the code for extracting and formatting language information:

```js
let language = codeBlock.getAttribute('data-language') || 'text';

const formatLanguage = lang => {
  const languageMap = {
    ts: 'TypeScript',
    js: 'JavaScript',
    tsx: 'TSX',
    jsx: 'JSX',
    md: 'Markdown',
    mdx: 'MDX'
  };

  return (
    languageMap[lang.toLowerCase()] ||
    lang.charAt(0).toUpperCase() + lang.slice(1)
  );
};
const displayLanguage = formatLanguage(language);
```

You can also add more languages to the `languageMap` object or keep it small as per your needs. This mapping covers popular programming languages and provides a fallback that capitalizes the first letter. Here’s the complete code for what I am using on this blog:

```js
const languageMap = {
  ts: 'TypeScript',
  js: 'JavaScript',
  tsx: 'TSX',
  jsx: 'JSX',
  md: 'Markdown',
  mdx: 'MDX',
  sh: 'Shell',
  bash: 'Bash',
  json: 'JSON',
  yaml: 'YAML',
  yml: 'YAML',
  toml: 'TOML',
  css: 'CSS',
  html: 'HTML',
  xml: 'XML',
  py: 'Python',
  rb: 'Ruby',
  cpp: 'C++',
  swift: 'Swift',
  kotlin: 'Kotlin'
};
```

## Create a header bar structure

The header bar is a visual container for both the language label and the existing copy button. Create a flexbox container to position the language label on the left appropriately and the copy button on the right. The code block below also uses the CSS classes integrated with my blog's theme system.

```js
let headerBar = document.createElement('div');
headerBar.className =
  'code-header flex items-center justify-between bg-skin-card border-b border-skin-border px-4 py-2 rounded-t-md';
```

## Build a language label

To provide clear identification of the code block's programming language, you need to create a language label provider:

```js
let languageLabel = document.createElement('span');
languageLabel.className = 'language-label text-sm text-skin-base font-medium';
languageLabel.innerHTML = displayLanguage;
languageLabel.setAttribute('aria-label', `Code language: ${displayLanguage}`);
languageLabel.setAttribute('role', 'note');
```

In the above code block, the accessibility attributes provide context for screen readers, with the `aria-label` explaining the purpose and the role attribute marking it as informational content.

## Create a copy button

The copy button is used to provide an interactive functionality for copying code to the clipboard. It utilizes a button element that incorporates hover effects and color transitions to leverage the existing theme system. Let’s also create a function to handle the actual copy functionality:

```js
let copyButton = document.createElement('button');
copyButton.className =
  'copy-code text-sm text-skin-base hover:text-skin-accent transition-colors font-medium';
copyButton.innerHTML = copyButtonLabel;
copyButton.setAttribute(
  'aria-label',
  `Copy ${displayLanguage} code to clipboard`
);
copyButton.setAttribute('title', 'Copy code to clipboard');

async function copyCode(block, button) {
  let code = block.querySelector('code');
  let text = code?.innerText;

  await navigator.clipboard.writeText(text ?? '');
  button.innerText = 'Copied';

  setTimeout(() => {
    button.innerText = copyButtonLabel;
  }, 700);
}
```

In the `copyCode` function, the Clipboard API writes the text to the user's clipboard asynchronously. Visual feedback changes the button text to "Copied" temporarily before reverting after 700 milliseconds, providing clear confirmation that the action was successful.

Here’s how the label and hover effect works on the label:



## Add the language label and copy button to the header

Next, add the language button and copy button to the header bar with the appropriate styling:

```js
headerBar.appendChild(languageLabel);
headerBar.appendChild(copyButton);
codeBlock.setAttribute('tabindex', '0');

codeBlock.className =
  (codeBlock.className || '') + ' rounded-t-none rounded-b-md';

codeBlock?.parentNode?.insertBefore(wrapper, codeBlock);
wrapper.appendChild(headerBar);
wrapper.appendChild(codeBlock);

copyButton.addEventListener('click', async () => {
  await copyCode(codeBlock, copyButton);
});
```

## CSS styles to render header bar and code block

In code block styles, ensure that the header bar and the actual code block appear as one unit when rendered on a blog page:

```css
@layer components {
  .code-header {
    @apply select-none;
    margin-bottom: 0 !important;
  }

  .language-label {
    @apply select-none pointer-events-none;
  }

  .copy-code {
    @apply cursor-pointer select-none;
  }

  .copy-code:hover {
    @apply transform scale-105;
  }

  pre {
    @apply mt-0 mb-4;
    position: relative;
    margin-top: 0 !important;
  }

  pre > code {
    @apply pt-0;
  }

  .code-header + pre {
    margin-top: 0 !important;
    border-top: none;
  }
}
```

## Wrap up

The improved code block now contains the language labels to provide immediate context about what kind of code a reader is looking at:





When switching to the dark theme, the header bar renders correctly:



---

## How to Animate a Header View on Scroll With React Native Animated
Slug: animate-header-view-on-scroll-with-react-native-animated-api

![cover_image](https://i.imgur.com/qDKlX8L.jpg)

The [Animated](https://reactnative.dev/docs/animated.html) library from React Native provides a great way to add animations and give app users a smooth and friendlier experience.

In this tutorial, let's explore a way to create a header view component that animates on the scroll position of the `ScrollView` component from React Native. We will go through the basics of creating a new Animated value as well as explaining the significance of functions and properties like `interpolation`, `extrapolate`, `contentOffset`, and so on.

[The source code is available at GitHub](https://github.com/amandeepmittal/react-native-examples/tree/master/animate-header-on-scroll).

## Prerequisites

To follow this tutorial, please make sure you are familiarized with JavaScript/ES6 and meet the following requirements on your local dev environment.

- [Node.js](https://nodejs.org/) version >= 12.x.x installed
- Have access to one package manager such as npm or yarn
- [expo-cli](https://github.com/expo/expo-cli) version installed or use npx

The example in the following tutorial is based on Expo SDK 38.

## Installing dependencies

Start by creating a new React Native app generated with `expo-cli`. Do note that all the code mentioned in this tutorial works with plain React Native apps as well. Open up a terminal window and execute the following command:

```shell
npx expo-cli init animate-header-example

# after the project is created, navigate into the directory
cd animate-header-example
```

To handle devices with notch both on iOS and Android operating systems, let's install some libraries first. These libraries are going to add automatic padding on notch devices such that the main view of the app does not intersect with a safe area on notch-enabled devices. Run:

```shell
expo install react-native-safe-area-view react-native-safe-area-context
```

To use safe area views, wrap the root of the React Native app with `SafeAreaProvider` from the [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context) library. Open `App.js` and modify the it as shown below:

```js
import React from 'react';
import { Text, View } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function App() {
  return (
    
      
        Open up App.js to start working on your app!
      
    
  );
}
```

Next, wrap the contents of the `App` component with `SafeAreaView` from the [react-native-safe-area-view](https://github.com/react-navigation/react-native-safe-area-view) library. It is going to have a `style` prop with a `flex` of value `1` and another prop called `forceInset`. It’s important we add this, especially for some Android devices which might not behave as expected. This prop is going to force the application to add an inset padding on the content view. Setting the value of `top: always` will always imply that padding is forced at the top of the view.

```js
// ... other import statements
import SafeAreaView from 'react-native-safe-area-view';

export default function App() {
  return (
    
      
        
          Open up App.js to start working on your app!
        
      
    
  );
}
```

Here is what happens on an Android device when `forceInset` is not used on `SafeAreaView`:

![ss1](https://i.imgur.com/uBdAKZ4.jpg)

And with the `forceInset` prop applied:

![ss2](https://i.imgur.com/xNZx2rq.jpg)

On iOS, the behavior is as expected:

![ss3](https://i.imgur.com/HXHJRv9.png)

The last step in this section is to create a new component file called `AnimatedHeader.js` inside the `components/` directory. For now, it is going to return nothing.

```js
import React from 'react';
import { Animated, View } from 'react-native';

const AnimatedHeader = () => {
  return null;
};

export default AnimatedHeader;
```

Make sure to import it in the `App.js` file:

```js
// ... after other import statements
import AnimatedHeader from './components/AnimatedHeader';
```

## Creating an animated header component

The animation on the position of the scroll on a `ScrollView` component is going to have an `Animated.Value` of `0`. To create an animation, `Animated.Value` is required. In the `App.js` file, import `useRef` from the React library. Then, define a variable called `offset` with a new `Animated.Value`. To use the Animated library from React Native, import it as well.

```js
import React, { useRef } from 'react';
import { Text, View, Animated } from 'react-native';
// ...other import statements

export default function App() {
  const offset = useRef(new Animated.Value(0)).current;

  // ...
}
```

For this example, it is not required to use the `useRef` hook; however, if you are looking forward to modifying the animated value, it is recommended to use `useRef`. It provides a `current` property that is persisted throughout a component's lifecycle.

The value of the `offset` can now be passed as a prop to the `AnimatedHeader` component.

```js
export default function App() {
  const offset = useRef(new Animated.Value(0)).current;

  return (
    
      
        {/* Add the following AnimatedHeader */}
        
        
          Open up App.js to start working on your app!
        
      
    
  );
}
```

To access the safe area inset value inside the `AnimatedHeader` component, the library `react-native-safe-area-context` provides a hook called `useSafeAreaInsets()`. This hook returns a safe area insets object with the following values:

```js
{
  top: number,
  right: number,
  bottom: number,
  left: number
}
```

The inset value of `top` is going to be manipulated when defining the animated header.

First, let's import this hook in the `AnimatedHeader.js` file and then define a fixed `HEADER_HEIGHT` constant that is going to be the initial height of the `Animated.View`.

```js
// ... other import statements
import { useSafeAreaInsets } from 'react-native-safe-area-context';

const HEADER_HEIGHT = 200;

const AnimatedHeader = ({ animatedValue }) => {
  const insets = useSafeAreaInsets();

  return null;
};
```

To animate the height of the header view on the scroll, we are going to use interpolation. The `interpolate()` function on `Animated.Value` allows an input range to map to a different output range.

In the current scenario, when the user scrolls, the interpolation on `Animated.Value` is going to change the scale of the header to slide to the top on scroll along the y-axis. This effect is going to minimize the initial value of the height of `Animated.View`.

The interpolation must specify an `extrapolate` value. This determines the scaling of the header’s height to be visible at the last value in `outputRange`. There are three different values for `extrapolate` available, but we are going to use `clamp`.

Begin by declaring a variable called `headerHeight` that is going to have the value of interpolation. The `Animated.Value` is the prop `animatedValue` coming from the parent component.

The `inputRange` is going to be `0` to the `HEADER_HEIGHT` plus the top inset. The `outputRange` is to be the `HEADER_HEIGHT` plus the top inset to the top inset plus `44`.

```js
const AnimatedHeader = ({ animatedValue }) => {
  const insets = useSafeAreaInsets();

  const headerHeight = animValue.interpolate({
    inputRange: [0, HEADER_HEIGHT + insets.top],
    outputRange: [HEADER_HEIGHT + insets.top, insets.top + 44],
    extrapolate: 'clamp'
  });

  // ...
};
```

Now, let's add an `Animated.View` to render from this component. It is going to use `position: absolute` to help cover the background behind the status bar as well as the same color as the whole header.

```js
const AnimatedHeader = ({ animatedValue }) => {
  // ...
  return (
    
  );
};
```

This section ends with the following output:

![ss4](https://i.imgur.com/kOdpwwL.png)

## Manipulating the ScrollView

In the `App.js` file, a `ScrollView` component is going to be displayed beneath the header component and, in return, it is going to display a list of mocked data.

For this example, I've prepared a bare minimum list of book titles in a separate file called `data.js`.

```js
const DATA = [
  {
    id: 1,
    title: 'The Hunger Games'
  },
  {
    id: 2,
    title: 'Harry Potter and the Order of the Phoenix'
  },
  {
    id: 3,
    title: 'To Kill a Mockingbird'
  },
  {
    id: 4,
    title: 'Pride and Prejudice'
  },
  {
    id: 5,
    title: 'Twilight'
  },
  {
    id: 6,
    title: 'The Book Thief'
  },
  {
    id: 7,
    title: 'The Chronicles of Narnia'
  },
  {
    id: 8,
    title: 'Animal Farm'
  },
  {
    id: 9,
    title: 'Gone with the Wind'
  },
  {
    id: 10,
    title: 'The Shadow of the Wind'
  },
  {
    id: 11,
    title: 'The Fault in Our Stars'
  },
  {
    id: 12,
    title: "The Hitchhiker's Guide to the Galaxy"
  },
  {
    id: 13,
    title: 'The Giving Tree'
  },
  {
    id: 14,
    title: 'Wuthering Heights'
  },
  {
    id: 15,
    title: 'The Da Vinci Code'
  }
];

export default DATA;
```

The next step is to import this file in `App.js`. Also, import the `ScrollView` component from React Native.

```js
//...
import { ScrollView, Text, View, Animated } from 'react-native';

import DATA from './data';
```

Next, modify the contents of the `App` component. The important prop to note below in the `ScrollView` component is the `onScroll` prop. Mapping gestures like scrolling directly to an animated value can be done by using `Animated.Event`. This type of event function is passed as the value to the `onScroll` prop.

`Animated.Event` accepts an array of objects as the first argument which is going to be the `contentOffset`, which tells the current position of the scrolling view. It changes every time the user scrolls up or down. The value of `contentOffset` along the y-axis is going to be the same `Animated.Value` that is used to interpolate the height of the `AnimatedHeader` component.

It is recommended that you pass the second argument of `useNativeDriver` in `Animated.Event` .

```js
export default function App() {
  const offset = useRef(new Animated.Value(0)).current;

  return (
    
      
        
        
          {DATA.map(item => (
            
              
                {item.title}
              
            
          ))}
        
      
    
  );
}
```

Here is the output after this step on an iOS device:

![ss5](https://i.imgur.com/QFWrJCN.gif)

On Android:

![ss6](https://i.imgur.com/4HlPFNQ.gif)

## Conclusion

I hope you had fun reading this tutorial. If you are trying the Animated library from React Native for the first time, wrapping your head around it might take a bit of time and that's the part of the process.

Some of the important topics covered in this post are listed as links for further reading below:

- [The onScroll prop](https://reactnative.dev/docs/scrollview#onscroll)
- [Interpolation](https://reactnative.dev/docs/animations#interpolation)
- [Tracking gestures with Animated.Event](https://reactnative.dev/docs/animations#tracking-gestures)

Originally published at [Jscrambler](https://jscrambler.com/blog/how-to-animate-a-header-view-on-scroll-with-react-native-animated).

---

## How to add an app icon in a React Native Android app
Slug: app-icon-react-native-android

![cover_image](https://i.imgur.com/tZtGF2K.png)

In this post, let's generate an app icon and learn how to add it to an Android app build with React Native.

## Generating an app icon

### Quickly build an app icon

To create an app icon we are going to make use of a free tool called [Expo Icon Builder](https://buildicon.netlify.app/). Thanks to [Evan Bacon](https://x.com/baconbrix?lang=en) for making it free and available for us to use. You are free to use any other design tool of your own choice such as Figma.

This tool allows building an app icon quickly using an Emoji icon with a customized background color scheme. For example, in the below image you will find that we have selected the coffee icon emoji since it is going to represent the main app. The Emoji icon is selected from the right-hand panel. The selected item is shown on the left-hand side. You can also add a customized background color.

![04-1](https://i.imgur.com/ulbO2nm.png)

After selecting the icon you have to download it by clicking the download button at the top left corner.

![04-2](https://i.imgur.com/dnvBU00.png)

### Generate different assets for Android

Android requires five separate sizes for different screen pixel densities. Icons for lower resolution are created automatically from the baseline (_mdpi_). Refer to the table below for more information on pixel densities:

|   Resolution    | Density | Pixel units |
| :-------------: | :-----: | :---------: |
| mdpi (Baseline) | 160 dpi |     1×      |
|      hdpi       | 240 dpi |    1.5×     |
|      xhdpi      | 320 dpi |     2×      |
|     xxhdpi      | 480 dpi |     3×      |
|     xxxhdpi     | 640 dpi |     4×      |

Another free service I like to use to generate different assets for the app icon is called [makeappicon.com](https://makeappicon.com/). This service generate different assets for various pixel densities based on the above table.

All you have to do is upload the app icon we created in the previous step and provide an email where the icons will be available to download.

![04-3](https://i.imgur.com/JHjruoR.png)

It creates icons for both platforms iOS and Android. For this demo app, we are only interested in the `android/` directory it generates. A different set of directories are created that are prefixed with `mipmap` and suffixed with different sizes like `hdpi` and `mdpi`.

![04-4](https://i.imgur.com/lKLlS1a.png)

## Where to place the icon?

Within an Android app, the icons live at the following path: `android/app/src/main/res`. Now copy the contents of the `android/` directory generated in the previous step and paste them to that location. Note that there going to be the same directory names. You will have to replace those old directories with the new ones to be pasted.

![04-5](https://i.imgur.com/myCrBZc.png)

## Should the icons be rounded or squared?

Depending on the Android device the system will decide whether to use a square icon or a rounded icon. You may need both types of the icon. For this demo app, we are going to use the icon we generated and exclude the rounded icon from the configuration of the Android app.

To not use the rounded icon, open `android/app/src/main/AndroidManifest.xml` file and remove the modify following line:

```xml
android:roundIcon="@mipmap/ic_launcher_round"
```

To the same icon generated in the previous step:

```xml
android:roundIcon="@mipmap/ic_launcher"
```

In the same file, you will find the code snippet: `android:icon="@mipmap/ic_launcher"` points towards the original icon filename.

You will have to run the build command `npx react-native run-android` to apply these changes to show the app icon.

![04-6](https://i.imgur.com/KYxddTW.jpg)

## Resources

That's it to generate and a new icon for your Android app. Please find the [link here](https://developer.android.com/google-play/resources/icon-design-specifications) to learn more about Google Play's icon design specifications.

---

## Atom: An Editor of 21st Century
Slug: atom-an-editor-of-21st-century

The [Atom](http://atom.io/) I am talking about is not a small particle in the world of developers. Built and maintained by Github and the community, it is more than just an editor.

In our world, in the world of developers, an editor plays an amount of significant role when it comes to writing code. They might seem just a tool, as a matter of fact, they are, but for someone who takes pleasure in writing code, it is an essential piece in their setup.

Using an editor is really a matter of choice, your comfort zone but the game of plugins have a big role to play. As yourself, I have tried my hands on different editors & IDEs and for now I have decide to settle with Atom for its enormous amount of plugins available. Another reason for me is that it’s written in JavaScript (exactly CoffeeScript & since I am a JS enthusiast) and is completely free to use because of its open source normality.

It is inspiring to open-source community as well. Few months back, Facebook released there version called Nuclide which focus more on mobile development. Then there is Electron for building cross-platform desktop application. Both are based on Atom Shell.

Atom is mature in terms of plugins when comes to keeping the pace with newer technologies. When I started out with Node.js, I tried to stick with Webstorm IDE but soon Atom lured me for its support of frameworks like Ionic (a plugin called Ionic-Preview and I am still exploring) thanks to the community plugins and the other matter of fact that it’s open source.

![1](https://i.imgur.com/a5Ziwot.jpg)

## Packages

There are more than 4.5k +packages published already. To view, Open the Settings view by clicking Packages > Settings View > Open in the menu bar or by using the (cmd + ,) keyboard shortcut.

Atom Packages are categorized further into:

- Community packages: Packages written by people outside of GitHub’s Atom team.
- Core packages: These are developed by the Atom team and come bundled with Atom but you can disable them if they are no use to you.
- Development packages: Packages which you have on your machine and use in Atom from that local source.

Core Packages do provide support for most favorable programming/scripting languages but if you are using something out of the box then you must check the Community packages. Chances of finding the one are in your favour there.

If you are still unable to find what you are looking, you should try developing a package for yourself and then if you like share it with the community.

## TIP- Decreasing Startup Time

There may come a period of time when your Atom might behave sluggishly. What will you do?

- You’ll remove all the packages (a.k.a plugins) you don’t use but for some reason you had them installed,
- or there isn’t much use of that particular package and you can do without it,
- or it was there just for fun. But before removing the packages, one must know which package is consuming how much startup time. You might want some of those unnecessary packages to stay with you if you know they are not the real culprits who are slowing down your development environment.

![2](https://i.imgur.com/HEqlVPv.png)

I have seen (mostly on online forums) some developers groan about this kind of sluggishness but I haven’t met it yet.

**Recommended Packages**

Most of these packages are helpful in my daily JavaScript coding environment. I think they will be helpful to you too.

- open-recent (to continue where you left)
- sync-settings (do you work more than one machine? This for you)
- Ionic-Preview (for people who are working Ionic Framework)
- terminal-plus (terminal inside atom)
- atom-jade (there is support for ejs, handlebars, mustache too)
- jade (if you are into jade, this is a snippets package)
- js-hyperclick (as your project goes bigger, you will get addicted to it)
- atom-lupa (nice one if your daily musings includes React)
- autocomplete-modules (Node.js devs: autocompletes require statements)
- atom-pair (developed by the folks at Pusher, a must if you do pair coding)
- javascript-snippets (JavaScript and Node.js snippets)
- atom-nodejs-snippets (this is another Nodejs & JavaScript snippet package, made by me. Supports ES6 syntax)
- linter + linter-jshint
- atom-ternjs (JavaScript code intelligence for Atom)
- encourage (a nice one to have, if you are having a dark day, fun)

The main advantage of an editor like Atom is that you can highly customize it. You can hack it, make your own packages, or use the one that are already there in the community and bend them as per your needs.

## Lastly

![3](https://i.imgur.com/HIgoDE2.jpg)

_Note: At the time of writing this post, Webstorm IDE did not have support for frameworks like Ionic and the latest Atom Version is 1.8.0._

---

## How Authentication Flow works in React Native apps using React Navigation 4.x
Slug: authentication-navigation-flow-in-react-native-apps

![cover_image](https://i.imgur.com/0xwhr2a.png)

Mobile apps are made of screens that in number can vary depending on the app you are developing. Handling user navigation can be tricky to learn and do in mobile apps, but with dedicated open-source libraries like [`react-navigation`](https://reactnavigation.org) do make the process a lot easier.

React Navigation library is common among React Native developers. It is built with JavaScript, and you can create React components and apply any navigation pattern. On the device, it will give the natural look and feel.

It is up to the developer now, on how to make the best use of navigation between different screens in a React Native app. There are more than one navigation patterns available. If you are starting in the React Native ecosystem, this post will guide you through to use different the patterns of navigation such as Stack and Switch navigation using `react-navigation` library's latest `4.x.x` version.

## Table of Contents

- Requirements
- Installing navigation library
- Create app screens
- Setup navigation
- Navigating between two screens
- Managing authentication flow
- Conclusion

## Requirements

If you are going to code along, make sure you have already installed the following:

- [Nodejs](https://nodejs.org) (>=`10.x.x`) with npm/yarn installed.
- [expo-cli](https://docs.expo.io/versions/latest/workflow/expo-cli/) (>= `3.x.x`), previously known as `create-react-native-app`.
- Mac users must be running an iOS simulator.
- Windows/Linux users must be running an Android emulator.

To know more about how to setup and run the simulator or the emulator on your local development environment visit React Native’s [official documentation here](https://facebook.github.io/react-native/docs/getting-started).

## Installing navigation library

To get started, create a new Expo app using `expo-cli` with the following command from a terminal window. When asked, choose the `blank` template.

```shell
expo init expo-example

# navigate inside the project directory
cd expo-example
```

Once inside the project directory, install the following dependencies.

```shell
yarn add react-navigation react-navigation-stack

expo install react-native-gesture-handler
react-native-screens
```

As compared to previous versions of `react-navigation`, all three navigation patterns have been modularised in their own dependencies. If you are using:

- stack navigation, then install `react-navigation-stack`
- for tabs install `react-navigation-tabs`
- for drawer install `react-navigation-drawer`
- switch navigation pattern is still under `react-navigation` and is only used for specific use cases such as authentication flow

More appropriate information about each dependency related to its own navigation pattern can be found in the [official docs here](https://reactnavigation.org/docs/en/stack-navigator.html).

After installing these dependencies, you can verify that they have been installed by opening the `package.json` file.

```json
"dependencies": {
    "expo": "^34.0.1",
    "react": "16.8.3",
    "react-dom": "^16.8.6",
    "react-native": "https://github.com/expo/react-native/archive/sdk-34.0.0.tar.gz",
    "react-native-gesture-handler": "~1.3.0",
    "react-native-reanimated": "~1.1.0",
    "react-native-screens": "1.0.0-alpha.22",
    "react-native-web": "^0.11.4",
    "react-navigation": "4.0.0",
    "react-navigation-stack": "1.5.1"
  },
```

## Create App Screens

I like to arrange different setups and related files under the folder structure. Here is how it is going to look like at the end of this tutorial. It is also a good practice to organize or give structure to your project.

![1](https://i.imgur.com/lkVcW5L.png)

The three files inside the `screens` folder are going to be functional components for now, with some dummy text to display. Create these files with the following code snippets.

For `Home.js`:

```js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function Home() {
  return (
    
      Home
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});
```

For `Login.js`:

```js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function Login() {
  return (
    
      Login
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});
```

For `Signup.js`:

```js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';

export default function Signup() {
  return (
    
      Signup
    
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center'
  }
});
```

The idea here is to create a foundation of three different screens and mock a login/signup and main (_in the current case, the home screen_) screen navigation pattern. This is a common pattern in most mobile apps where the user has to either signup or login before accessing the rest of the application.

## Setup Navigation

After creating these three screens, create a new directory called `navigation`. Inside this, create three new files:

- `index.js`
- `AppNavigation.js`
- `AuthNavigation.js`

Let us setup the `AppNavigation` first since it will contain only one screen. Open up the file and add the following code.

```js
//AppNavigation.js
import { createStackNavigator } from 'react-navigation-stack';
import Home from '../screens/Home';

const AppNavigation = createStackNavigator(
  {
    Home: { screen: Home }
  },
  {
    initialRouteName: 'Home'
  }
);

export default AppNavigation;
```

Stack Navigation provides your app to navigate between screens, where each new screen is placed on the top of the previous one. It is literally like a stack and hence the name. This is done by `createStackNavigator` function. A route configuration object is passed to this function. The `Home` route corresponds to the `Home.js` component.

On an iOS device, a new screen slides from the right, and on Android, it fades from the bottom.

Next, edit `AuthNavigation.js` file.

```js
//AuthNavigation.js
import { createStackNavigator } from 'react-navigation-stack';
import Login from '../screens/Login';
import Signup from '../screens/Signup';

const AuthNavigation = createStackNavigator(
  {
    Login: { screen: Login },
    Signup: { screen: Signup }
  },
  {
    initialRouteName: 'Login'
  }
);

export default AuthNavigation;
```

Similarly, in `AuthNavigation` two screens, login and signup are passed. In the second object that is passed to `createStackNavigator` function, the `initialRouteName` indicates that when this navigation file runs, the first screen that will be shown is going to be `Login`. In other words, it is used to set a default screen to whatever the value `initialRouteName` is set to.

In `AppNavigation` since there is only one screen, so it will always show `Home` screen whether to pass the `initialRouteName` in that file or not. Next, open `index.js` file in the same directory and add the following code.

```js
//index.js
import { createAppContainer } from 'react-navigation';
import AuthNavigation from './AuthNavigation';

const AppContainer = createAppContainer(AuthNavigation);

export default AppContainer;
```

The `createAppContainer` function is responsible for managing the navigation state of the app and links the app to the top-level navigator. The navigation state comes in handy when you are passing data between two screens.

Lastly, open the `App.js` file and use `AppContainer` to be the top-level component.

```js
//App.js
import React from 'react';
import AppContainer from './navigation';

export default function App() {
  return ;
}
```

Now open your app in a simulator device by executing the command `expo start` from a terminal window. You will see that it shows only the `Login` screen.



_Notice the empty space at the top of the screen?_ That is the header section. When using Stack Navigation pattern, each screen is assigned a header automatically. If you do not require to use it, you can set the `headerMode` property to the value of `none` to `createStackNavigator` function. Open `AuthNavigation.js` to edit.

```js
// AuthNavigation.js
const AuthNavigation = createStackNavigator(
  {
    Login: { screen: Login },
    Signup: { screen: Signup }
  },
  {
    initialRouteName: 'Login',
    headerMode: 'none'
  }
);
```

You can read more about app containers [here](https://reactnavigation.org/docs/en/app-containers.html#props-of-createappcontainer-on-react-native).

## Navigating between two screens

Right now, there is no way you can navigate from the `Login` to the `Signup` screen. To do so, let us use `this.props.navigation`. Each screen component in the app using `react-navigation` library is automatically provided with the `navigation` prop. It further has [different reference values](https://reactnavigation.org/docs/en/navigation-prop.html) to navigate between different screens directly from a screen.

To transit between login to signup, create a button like below and pass an `onPress` prop to it in `Login.js` file. The value of this prop is going to hold the `navigation` prop reference.

```js
//Login.js

//import Button
import { StyleSheet, Text, View, Button } from 'react-native';

export default class Login extends React.Component {
  render() {
    return (
      
        Login
        
        
      
    );
  }
}

export default withStyles(styles)(Signin);
```

This is a form component that contains `email` and `password` field (\_as we defined in state above) for the user to enter to get authenticated. `redirectToReferrer` property in state is what we are using if the user gets verified by the server or not. If the credentials entered by the user are valid, this property will trigger `Redirect` component of `react-router-dom`.

### Front-End: User Components

Similarly to our auth routes, we are going to separate our user components inside `components/user/` folder. First, we need a React component to register a new user. Create a file called `Signup.js`.

```js
import React, { Component } from 'react';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import Icon from '@material-ui/core/Icon';
import { withStyles } from '@material-ui/core/styles';
import DialogTitle from '@material-ui/core/DialogTitle';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogContent from '@material-ui/core/DialogContent';
import Dialog from '@material-ui/core/Dialog';
import { Link } from 'react-router-dom';

import { registerUser } from '../../utils/api-user.js';

const styles = theme => ({
  card: {
    maxWidth: 600,
    margin: 'auto',
    textAlign: 'center',
    marginTop: theme.spacing.unit * 5,
    paddingBottom: theme.spacing.unit * 2
  },
  error: {
    verticalAlign: 'middle'
  },
  title: {
    marginTop: theme.spacing.unit * 2,
    color: theme.palette.openTitle
  },
  textField: {
    marginLeft: theme.spacing.unit,
    marginRight: theme.spacing.unit,
    width: 300
  },
  submit: {
    margin: 'auto',
    marginBottom: theme.spacing.unit * 2
  }
});

class Signup extends Component {
  state = {
    name: '',
    password: '',
    email: '',
    open: false,
    error: ''
  };

  handleChange = name => event => {
    this.setState({ [name]: event.target.value });
  };

  clickSubmit = () => {
    const user = {
      name: this.state.name || undefined,
      email: this.state.email || undefined,
      password: this.state.password || undefined
    };
    registerUser(user).then(data => {
      if (data.error) {
        this.setState({ error: data.error });
      } else {
        this.setState({ error: '', open: true });
      }
    });
  };

  render() {
    const { classes } = this.props;
    return (
      
Sign Up


{' '} {this.state.error && ( error {this.state.error} )}
New Account New account successfully created.
); } } export default withStyles(styles)(Signup); ``` We start the component by declaring an empty state that contains various properties such as name, email, password and error. The `open` property is used to capture the state of a `Dialog` box. In Material UI, a `Dialog` is a type of modal window that appears in front of app content to provide critical information or ask for a decision. The modal in our case will either render an error message or the confirmation message depending on the status returned from the server. We are also defining two handler functions. `handleChange` changes the new value of every input field entered. `clickSubmit` invokes when a user after entering their credentials, submit the registration form. This function calls `registerUser` from the API to send the data to the backend for further actions. Create a new file called `Profile.js`. ```js import React, { Component } from 'react'; import { withStyles } from '@material-ui/core/styles'; import Paper from '@material-ui/core/Paper'; import ListItem from '@material-ui/core/ListItem'; import ListItemAvatar from '@material-ui/core/ListItemAvatar'; import ListItemText from '@material-ui/core/ListItemText'; import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; import Avatar from '@material-ui/core/Avatar'; import Typography from '@material-ui/core/Typography'; import Person from '@material-ui/icons/Person'; import Divider from '@material-ui/core/Divider'; import auth from '../auth/auth-helper'; import { findUserProfile } from '../../utils/api-user.js'; import { Redirect, Link } from 'react-router-dom'; import DeleteUser from './DeleteUser'; const styles = theme => ({ root: theme.mixins.gutters({ maxWidth: 600, margin: 'auto', padding: theme.spacing.unit * 3, marginTop: theme.spacing.unit * 5 }), title: { margin: `${theme.spacing.unit * 3}px 0 ${theme.spacing.unit * 2}px`, color: theme.palette.protectedTitle } }); class Profile extends Component { constructor({ match }) { super(); this.state = { user: '', redirectToSignin: false }; this.match = match; } init = userId => { const jwt = auth.isAuthenticated(); findUserProfile( { userId: userId }, { t: jwt.token } ).then(data => { if (data.error) { this.setState({ redirectToSignin: true }); } else { this.setState({ user: data }); } }); }; componentWillReceiveProps = props => { this.init(props.match.params.userId); }; componentDidMount = () => { this.init(this.match.params.userId); }; render() { const { classes } = this.props; const redirectToSignin = this.state.redirectToSignin; if (redirectToSignin) { return ; } return ( Profile {' '} {auth.isAuthenticated().user && auth.isAuthenticated().user._id == this.state.user._id && ( )} ); } } export default withStyles(styles)(Profile); ``` This component shows a single user who is authenticated by the back-end of our application. The profile information of each user is stored in the database. This is done by the `init` function we have defined above the render function of our component. We are using `redirectToSignin` redirect to the user on sign-out. We are also adding a delete profile button as a separate component which has to be defined in a separate file called `DeleteUser.js`. ```js import React, { Component } from 'react'; import IconButton from '@material-ui/core/IconButton'; import Button from '@material-ui/core//Button'; import DialogTitle from '@material-ui/core/DialogTitle'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContentText from '@material-ui/core/DialogContentText'; import DialogContent from '@material-ui/core/DialogContent'; import Dialog from '@material-ui/core/Dialog'; import Delete from '@material-ui/icons/Delete'; import auth from '../auth/auth-helper'; import { deleteUser } from '../../utils/api-user'; import { Redirect, Link } from 'react-router-dom'; class DeleteUser extends Component { state = { redirect: false, open: false }; clickButton = () => { this.setState({ open: true }); }; deleteAccount = () => { const jwt = auth.isAuthenticated(); deleteUser( { userId: this.props.userId }, { t: jwt.token } ).then(data => { if (data.error) { console.log(data.error); } else { auth.signout(() => console.log('deleted')); this.setState({ redirect: true }); } }); }; handleRequestClose = () => { this.setState({ open: false }); }; render() { const redirect = this.state.redirect; if (redirect) { return ; } return ( {'Delete Account'} Confirm to delete your account. ); } } export default DeleteUser; ``` This component is used for deleting the user profile that exists in the database. It uses the same `deleteUser` API endpoint we defined in our back-end. `deleteAccount` method is responsible for handling this task. ### Front-End: Completing the Navbar In this section we are going to complete our client side routes by leveraging a `Navbar` component. Create a new file `component/Navbar.js`. ```js import React from 'react'; import AppBar from '@material-ui/core/AppBar'; import Toolbar from '@material-ui/core/Toolbar'; import Typography from '@material-ui/core/Typography'; import IconButton from '@material-ui/core/IconButton'; import Home from '@material-ui/icons/Home'; import Button from '@material-ui/core/Button'; import auth from './auth/auth-helper'; import { Link, withRouter } from 'react-router-dom'; const isActive = (history, path) => { if (history.location.pathname == path) return { color: '#F44336' }; else return { color: '#ffffff' }; }; const Menu = withRouter(({ history }) => ( MERN App {!auth.isAuthenticated() && ( )} {auth.isAuthenticated() && ( )} )); export default Menu; ``` This `Navbar` component will allow us to access routes as views on the front-end. From `react-router` we are importing a High Order Component called `withRouter` to get access to history object's properties and consume our front-end routes dynamically. Using `Link` from `react-router` and `auth.isAuthenticated()` from our authentication flow, we are checking for whether the user has access to authenticated routes or not, that is, if they are logged in to our application or not. `isActive` highlights the view to which the current route is activated by the navigation component. ### Running the Application The next step is to import this navigation component inside `Routes.js` and define other necessary routes we need in our app. Open `Routes.js` and add the following. ```js import React, { Component } from 'react'; import { Route, Switch } from 'react-router-dom'; import Navbar from './components/Navbar'; import Home from './components/Home'; import PrivateRoutes from './components/auth/PrivateRoutes'; import Signin from './components/auth/Signin'; import Profile from './components/user/Profile'; import Signup from './components/user/Signup'; class Routes extends Component { render() { return (
); } } export default Routes; ``` After completing this test, let’s test our application. Make sure you are running the backend server using `nr dev` command in one tab in your terminal. Using another tab or window, traverse to `client` directory and run the command `yarn start`. Once the application starts, you will be welcomed by the Homepage, as below. Notice in the navbar above there are three buttons. The home icon is for Home page highlighted `red` in color. If you move on to the sign in page, you will see the sign in button highlighted. We already have one user registered to our application (_when we were building the API_). Please enter the credentials (_email:_ [_jane@doe.com_](mailto:jane@doe.com) and _password: pass1234 or the credentials you entered_) as shown below and submit the form. On submitting the form you will be redirected to the home page as per the component logic. The changes can be noticed at the navigation menu. Instead of sign-up and sign-in, you will see My Profile and Sign Out button. Click My Profile and you can see the current user’s details. On clicking the `delete` icon it will delete the user. You can also try signing out of the application by clicking on the sign out button from navigation and then you will be redirected to the home page. ### Conclusion We have reached the end. Even though this tutorial is lengthy and, a lot is going on, I am sure if you take your time, you will understand the concepts and the logic behind it. It is after all, a full-stack MERN application. It uses JSON Web Tokens as an authentication strategy. If you want to learn **how to deploy this application, you can continue to read** [**this article**](https://amanhimself.dev/blog/deploy-a-mern-stack-app-on-heroku)**.** **The complete code for the tutorial at [this Github repository](https://github.com/amandeepmittal/mern-material-demo)** --- ## Building a React Native Mobile App with AWS Amplify and Expo Slug: building-a-react-native-mobile-app-with-aws-amplify-and-expo ![cover](https://i.imgur.com/G1PqZSM.png) There is a joke going in dev community about serverless tech stacks using servers?! Some of the trolls even take that this an offensive way. What are they missing out is the advantages Serverless computing has to offer. Advantages like reduced development time and operation costs are some of the factors that could not be overlooked. Spending time and energy writing and wiring your application is worth rather than continuously managing resources and then worry about them at the time of scaling. This might sound too hot to some but serverless is a pragmatic solution in some use cases. In this tutorial, you will be learning how to integrate a serverless computing service called AWS Amplify in a mobile app using React Native. AWS Amplify has a complete set of toolchain with authentication, a choice between wiring and managing GraphQL or REST API, data storage, push notification and analytics. ### TLDR - What is AWS Amplify? - Requirements - Getting Started - Configure Amplify User - Initialize AWS Amplify - Add a GraphQL API - Publish API to AWS Cloud - Integrating Expo app with Amplify SDK - Adding a Todo Input Field - Adding a Mutation using Graphql API - Run Query to fetch data - Conclusion ## What is AWS Amplify? Amazon Web Service is a well-known technology that provides cloud services. Since its launch in 2017, Amplify has come a long way in terms of providing a definitive toolchain. The first attraction for me personally is that it is open source. Next, are the CLI plugins and services that you can enable with one click when integrating it in a framework like React Native (_or any other_). Services such as support for GraphQL and REST APIs, basic UI components to get you started, authentication HOCs, storage, hosting and many more are available in its toolchain. ## Requirements Here is a complete list of plugins, packages, services you are going to need in order to gain something from this tutorial. - [NodeJS](https://nodejs.org) `v8.x.x` or higher installed along with `npm/yarn` - [`watchman`](https://facebook.github.io/watchman/docs/install.html) the file change watcher for React Native project - AWS account - Amplify CLI plugin - Expo CLI (\_earlier known as `create-react-native-app`) _Note_: To use any Amplify service and to follow the rest of this tutorial, you need an AWS account (_which is free_). If you do not have one, please consider signing up for one [here](https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation) for the free tier. ## Getting Started After you have the first three requirements from the previous section let us install the last two. Both of them are command line plugins to scaffold and configure our React Native + AWS Amplify project. Open a terminal window and execute the following command. ```shell npm install -g @aws-amplify/cli expo-cli ``` Once both of the CLIs are installed, make sure you are on the same version (_at least the major one_) as we are. ```shell amplify --version # Output 1.6.6 expo-cli --version # Output 2.15.4 ``` Next, let us create a new React Native project using Expo CLI. ```shell expo-cli init expo-amplify-demo ``` It will then prompt for a few questions. The first and most important one is choosing a workflow. Choose the default option, that is `blank`. Then it will prompt you to enter the name of your app, you can leave it to default or enter a name. You can use `yarn` to install dependencies by pressing `Y`. After a few moments, a new project directory will appear on the desired location. Traverse inside it before we proceed to the next step. ## Configure Amplify User Once you are signed-in to AWS console, open up a terminal window and run the following command. ```shell amplify configure ``` This will open up the AWS console dashboard. Go back to terminal and press enter to continue. Next, are going to be a bunch of questions in order to configure a user account to use Amplify with your React Native application. These questions are as following: - **Choose a region:** us-east-2 - **Specify the username of the new IAM user:** expo-amplify-demo On entering the username, press enter and it will open AWS console again in a browser window for you to add a user. In the above screen, make sure that **Programmatic access** is checked. It allows adding the newly created user to have access to create resources in the form of different APIs and tools by providing you with an access key and secret key. Then click on button **Next: Permissions**. In the above screen, you will notice that a policy has been selected by default. Let it be. This provides you the full access to AWS services by enabling the aws user (_the current user you are creating_) to be an administrator. Then, click on **Next: Tags**. Leave this one blank, and click on **Next: Review**. Click on **Create user** on the next page and you will be directed to a new page where you will find **Access Key** and **Secret Key**. Do not close this window yet. Go to your terminal window, press the Enter key and it will ask you for the Access Key and the Secret Key. Enter both of them sequentially. Lastly, it will ask you about the profile name. You can enter the project name or user name here. Pressing enter for the last time will create a new AWS user. This section is complete. ## Initialize AWS Amplify To integrate AWS Amplify with the React Native app run the following command and be ready to answer a few more questions 😄. _I know, I know_. But imagine, not having these questions. The amount of setup being performed right now just by answering a few questions and pressing enters' a few times adds a lot of value by saving developer time. Open a terminal window, and make sure you are inside the React Native/Expo directory. ```shell amplify init ``` This command will help you setup amplify SDK inside the React Native app. First, a few sets of questions that are prompted can be seen below. Next, you will be prompted with a question on whether to use an AWS profile or not. You have to choose `Yes` and then on to the next question, choose the user name that you created in the previous steps when configuring amplify. If you are setting up for the time, you are probably going to have only one username in the list, unlike below. After the amplify SDK initialization process is complete, notice there are some new file changes inside the project directory. A new directory `amplify/` and a new file `aws-exports.js` The `amplify` directory takes care of configuration files that required in order to setup and makes amplify SDK work with the current React Native app. These configuration files are further divided into two parts. One set of files are just for your local machine and another is for aws cloud. Please remember, whenever you make changes related to amplify SDK in your app, they are, by default, modifications made to the local part or development part. When you are done making modifications and are ready to deploy your changes to the cloud, you use special amplify commands such as `push`. After running this `push` command, only the changes are written in aws cloud. The file `aws-exports.js` contains details related to amplify cloud service and credentials for the SDK to be imported inside the React Native app. You will be using this file later on. ```js // WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten. const awsmobile = { aws_project_region: 'us-east-2' }; export default awsmobile; ``` ## Add a GraphQL API The idea of this section is that in your React Native app, you will be having an API that performs CRUD operations. CRUD stands for _Create, Read, Update and Delete_. Amplify toolchain makes this process easier using its own backend and data storing capabilities. Amplify supports HTTP requests to REST and GraphQL endpoints. Using AWS AppSync, you can easily build data-driven applications in real-time with offline capabilities. To set up an entire API for the app all you have to do is execute the below command. ```shell amplify add api ``` This CLI execution automatically creates a fully functional GraphQL API including data sources, resolvers with basic schema structure for queries, mutations, and subscriptions, downloads client-side code and configuration files that are required in order to run these operations by sending requests. The above command will prompt you to choose between what type of API you want to write in. Choose **GraphQL**, and enter a **profile API name**. Next, it will again, give you two options to choose as to how you want to authenticate your AWS AppSync API. In a real-time application, you will have different users accessing the database and making requests to it. For that, you will always go with **Amazon Cognito User Pool**. This is more of a pragmatic approach. That step needs authentication process and we will be covering that in a future post. For the current demo, choose the option **API Key**. Do note that this option is only for brief prototype sessions or development process. Any AppSync API key expires after seven days lifecycle. For the next question **Do you have an annotated GraphQL schema?** the answer is `N` or no. Amplify comes with pre-defined schemas that can be changed later. Press `Y` for the next question: **Do you want a guided schema creation?** Select `single object with fields`. Next, it will ask if you want to edit the GraphQL Schema. Say yes to that for now. This will open up a new file called `schema.graphql` which contains a schema of type `Todo` with a different set of fields. This step does create a new folder inside `amplify/backend/api/` that further contains the metadata information related to GraphQL API. Here is the model inside `schema.graphql` file. ```graphql type Todo @model { id: ID! name: String! description: String } ``` If you are not familiar to GraphQL models and its types here is brief information about them. A `type` in a GraphQL schema is that piece of data that is stored in the database. Each `type` can have a different set of fields. You can think of a `type` as an object coming from the JavaScript background. For example, in the above schema for `Todo` model is the `type` that has three fields: `id`, `name` and `description`. Also, `@model` is used for storing types in Amazon DynamoDB. This is the database is used by Amazon when storing our app data. Every `type` in a database generates a unique identity to each piece of information stored to further identify and persist in CRUD operations through HTTP requests. The `id` in this case is generated by Amplify and has a value of a built-in type of `ID` which, in GraphQL terminology, is known as a scalar type. You can read more about the different types identified in a GraphQL schema [here](https://graphql.org/graphql-js/basic-types/). The exclamation mark `!` signifies that the field is required when storing the data and it must have value. In the above schema, there are two required fields: `id` and `name` for the `Todo` type. Save this file, go back to the terminal window and press enter. You will be prompted with a success message (_probably, in green_). All the changes you have just made are now saved locally. ## Publish API to AWS Cloud To publish all the changes you have made (or left it default) in the local environment to AWS Cloud, run the command `amplify push`. On running the command, as a prompt, you get a table in return with information about resources that you have used and modified or enabled. The name of these resources is described in the Category section. The **Resource name** in the above table is the API name you choose in the previous section. Next column is the type of operation for the API to be sent, that is currently, **Create**. The provider plugin column signifies that these resources are now being published to the cloud. Press `Y` to continue. Amplify CLI interface will now check for the schema and then compile it for any errors before publishing final changes to the cloud. In the next step, it prompts whether you want to generate code for your newly created GraphQL API? Press `Y`. Then choose javascript as the code generation language. If you are using `TypeScript` or `flow`, now is the time to pick one. In the above image, for the last question, press `Y`. This will create a new folder inside the src directory which contains GraphQL schema, query, mutations, subscriptions as JavaScript files. On operating the API, these files can be accessible for different operations later. Press `Y` to the next question that asks you to update all GraphQL related operations. Also, let maximum statement depth be the default value of `2`. It will take a few moments to update the resources on the aws cloud and will prompt with a success message when done. At the end of the success message you will get a GraphQL API endpoint and a GraphQL API Key (_which we learned previously that it expires on the 7th day_). You do not have to save it somewhere on your desktop and panic. This information is added to `aws-exports.js` file automatically for you now. ## Integrating Expo app with Amplify SDK To make use of amplify SDK in the React Native app, install the following dependencies. ```shell yarn add aws-amplify aws-amplify-react-native ``` The package `aws-amplify` allows making requests to auth and API services provided AWS. The other package `aws-amplify-react-native` is specific to React Native as a library that contains useful components to be used in a project. You can verify that both of these packages were installed by peeking into `package.json` file > `dependencies`. ```json "dependencies": { "aws-amplify": "^1.1.26", "aws-amplify-react-native": "^2.1.10", "expo": "^32.0.0", "react": "16.5.0", "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz" }, ``` Open `App.js` and add the configuration keys from `aws-exports-.js` and make amplify SDK aware of them. ```js // App.js import React from 'react'; import { StyleSheet, Text, View } from 'react-native'; // ---------This is the part to add import Amplify from 'aws-amplify'; import config from './aws-exports'; Amplify.configure(config); // ----------------------------- ``` That's it for the integration part. Now let us write some GraphQL interactions and make sure it works with our React Native app in real-time. ## Adding a Todo Input Field To capture the user input, we are going to use a component state as follows. Add the below before the render method inside the `App` component. ```js //App.js state = { name: '', todos: [] }; ``` In the above state, there is a `name` field of the todo item and an array called `todos` that will be used to fetch all the todo items from the GraphQL API and display on the UI. Note that, there is another field called `description` in the GraphQL schema but since it isn't required, we are not going to use it here. Next, import `TextInput` and `TouchableOpacity` to create an input field and native button. Here is the complete code for `App.js`. ```js import React from 'react'; import { StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native'; import Amplify from 'aws-amplify'; import config from './aws-exports'; Amplify.configure(config); export default class App extends React.Component { state = { name: '', todos: [] }; onChangeText = (key, val) => { this.setState({ [key]: val }); }; addTodo = () => {}; render() { return ( this.onChangeText('name', val)} placeholder="Add a Todo" /> Add + ); } } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', paddingHorizontal: 10, paddingTop: 50 }, input: { height: 50, borderBottomWidth: 2, borderBottomColor: 'blue', marginVertical: 10 }, buttonContainer: { backgroundColor: '#34495e', marginTop: 10, marginBottom: 10, padding: 10, borderRadius: 5, alignItems: 'center' }, buttonText: { color: '#fff', fontSize: 24 } }); ``` Go to the terminal window and run the command `npm start` to view this either in an iOS simulator or an android emulator. You will get the following result. ## Adding a Mutation using Graphql API A _mutation_ in GraphQL is all about handling operations like adding, deleting or modifying data. Currently, the React Native application is basic but it serves the purpose of making you familiar with amplify as a toolchain and its integration with the cross-platform framework. To add a todo item and to retrieve the same you need some business logic to communicate with GraphQL backend. Let us start with a mutation. In the file `App.js`, import `API` and `graphqlOperation` from `aws-amplify`. Here, `API` is the category for AWS resource and the later is the method to run either a mutation or the query. Inside the `src/graphql/mutation.js` file you will find some mutation functions that we can make use of to create, delete, or update a note in the database. Also import `createTodo` from this file. ```js //App.js // ... import Amplify, { API, graphqlOperation } from 'aws-amplify'; import config from './aws-exports'; import { createTodo } from './src/graphql/mutations'; // ... ``` Add a function `addTodo` before the `render` method which uses `API.graphqlOperation()` the method from amplify SDK. This method will intake the mutation as the first argument and whatever input user enters inside the app UI, as the second argument. ```js // App.js addNote = async event => { const { name, todos } = this.state; event.preventDefault(); const input = { name }; const result = await API.graphql(graphqlOperation(createTodo, { input })); const newTodo = result.data.createTodo; const updatedTodo = [newTodo, ...todos]; this.setState({ todos: updatedTodo, name: '' }); }; ``` The above function takes `name` as the input where `name` is the text of a todo item. Also, notice the use of `async/await`. This helps to fetch the result from the mutation and update the `todos` array in the state with the latest todo item and previous or existing data in that array. After updating the state, clear the value of the input field `name`, and display in the UI by setting it back to an empty string. I urge you to add at least one list of item. You would not get any confirmation right now from the API whether the data field has been added to the GraphQL backend or not. ## Run Query to fetch data If you want to read data (_and render it in the UI of the app_), the process is known as a _query_. To fetch all the data from GraphQL API and display it on the device's screen, let us use the query from amplify GraphQL pre-generated file inside `src/graphql/queries.js` (_just like we did with mutation_). ```js // eslint-disable // this is an auto generated file. This will be overwritten export const getTodo = `query GetTodo($id: ID!) { getTodo(id: $id) { id name description } } `; export const listTodos = `query ListTodos( $filter: ModelTodoFilterInput $limit: Int $nextToken: String ) { listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) { items { id name description } nextToken } } `; ``` Import `listTodos` inside `App.js` from the above file. ```js //App.js import { listTodos } from './src/graphql/queries'; ``` We need to fetch the data at the time where the component gets rendered. For this, let us a lifecycle method called `componentDidMount`. Since this is going to be an asynchronous operation `async/await` is being used here too. Just after the state is defined in the `App` component, add the following snippet of code. ```js // App.js async componentDidMount() { try { const todos = await API.graphql(graphqlOperation(listTodos)) console.log("todos: ", todos) this.setState({ todos: todos.data.listTodos.items }) } catch (err) { console.log("error: ", err) } } ``` Refresh the app by saving the file you will notice that on UI screen nothing happens. That's because we haven't added the rendering logic to display this list of items. However, you can verify that data is being fetched using `console` statement and by looking quickly at the Expo CLI. During the previous step, I did add an item to the list. That's the proof of that. Now let us display this item on the device's screen. Inside the render method, add this after the `TouchableOpacity` component. We are going to use JavaScript's `map` function to traverse the `todos` array. ```js // App.js {this.state.todos.map((todo, index) => ( {todo.name} ))} // Corresponding styles for above jsx todo: { borderBottomWidth: 1, borderBottomColor: "#ddd", paddingVertical: 10 }, name: { fontSize: 16 } ``` On running `npm start` (_or if it is already running, just the save the App.js file_) you will get the following output. ## Conclusion This tutorial is complete. I am sure by now you have gained enough knowledge to build your own React Native app with AWS Amplify and AppAsync. Also, did you notice the amount of code written inside `App.js`? It is far less than a traditional app that uses self-backend techniques. This bare minimum demo can serve you a lot better. You can find the complete code for this post in this [Github repository](https://github.com/amandeepmittal/expo-amplify-demo). [Originally published at Heartbeat](https://heartbeat.fritz.ai/building-a-react-native-mobile-app-with-aws-amplify-and-expo-fcab6ee0555e) --- ## Building a REST API with Koajs Slug: building-a-rest-api-with-koajs ![cover](https://i.imgur.com/ohg3d3s.png) > [Originally published at Crowdbotics](https://medium.com/crowdbotics/building-a-rest-api-with-koajs-417c276929e2) There are quite a few [Node.js](http://crowdbotics.com/build/node-js?utm_source=medium&utm_campaign=nodeh&utm_medium=node&utm_content=koa-rest-api) frameworks available for web development to build a back-end server for a web or a mobile application. The most popular framework is ExpressJS, which has been used widely in the industry for a long time. In this article, however, **we are going to discuss** [**Koa**](http://koajs.com)**, to write server-side code that uses Node.js as the runtime engine. In this tutorial, I will show how to build a** [**small REST API**](https://blog.crowdbotics.com/how-to-write-an-api-in-3-lines-of-code-with-django-rest-framework/) **then test it using a REST Client.** ### What is Koa? Koa is designed and built by the team behind ExpressJS with additions such as promises and `async/await` support in its core. These ES2015 features are used to tackle API's asynchronous calls. Distributed as a lightweight Node.js framework, Koa provides a minimal interface for developing web applications and APIs. Koa has features that help JavaScript developers who want to use and leverage Node.js to accelerate the development of APIs and web applications. I have been using Koa for some of my latest back-end applications and I would love to share my knowledge to get you started. ### Features of Koa Some of the features of the Koa framework include: - Designed as a lightweight and flexible framework for Node.js - Support for ECMAScript 6 (/ES2015) by default - Developers can use generators as functions to stop and resume the code execution - Simplifies the use of Error handling by using middleware more effectively - Identifies and understands all HTTP methods - Even before Express, Koa had support for `async/await` To use this framework, the only two requirements for running the code examples below are to have Node.js installed in your local machine along with `npm` to access and install it as a dependency. The second requirement is to have a general understanding of JavaScript as a programming language. ### Getting Started To start using Koa as a server-side framework you will have to first install it. Create a directory where you will want to place all the project-related files. We will first initialize it as a Node.js project. ```shell # First initialize an empty directory npm init -y ``` This will help us generate a `package.json` file that holds all the records of dependencies we add to our project using `npm`. I am using the `-y` flag to skip the questionnaire prompted by npm. You will get a similar result once it is done. The next step is to add Koa as a local dependency. I am sure you know what a local dependency here means. If not, please refer to `[npm](https://docs.npmjs.com/)` [documentation](https://docs.npmjs.com/). ```shell npm install -S koa ``` So far so good. Now we can get started and build our application. However, please note that to use Koa either on your local machine or deploy any server-side project that uses it, _you need to have a Node.js version equal to or greater than_ `_v7.6.0_`_._ ### Our First Koa App To understand Koa better, and point out the differences with a commonly used framework such as ExpressJS, we are going to first write an obligatory _Hello World_ program. Below, we create a route in a file called `app.js`. ```js // app.js const Koa = require('koa'); const app = new Koa(); // Our First Route app.use(async ctx => { ctx.body = 'Hello World'; }); // Bootstrap the server app.listen(3000); ``` Now, open the terminal and run the following command: ```js node app.js ``` If you are not prompted with an error, that means the server ran successfully. Right now, we are not getting anything exciting from the terminal. If you go to `http://localhost:3000` in your browser window, you should see a `Hello World` message greeting you! To understand more about what is happening, let’s import the Koa library into our `app.js` file. Next, we define an instance called `app` that will access all the methods that are included in Koa's API such as `use()` and `listen()`. `app.use()` is how th middleware function is defined. We are using this middleware function as a route. The `app.listen()` is how the server knows to run on a port number specified such as `3000`. ### Wait, what is ctx? Another important feature that we use in our bare minimum example is `ctx`. I do hope you noticed it there. We are using `ctx` as an argument to the asynchronous middleware function. It is called **Context** in Koa and it encapsulates **request** and **response** objects into a single object. Unlike ExpressJS, that requires request and response as separate objects passed as arguments. For example: ```js // request and response objects in ExpressJS app.use((request, response) => { // ... rest of the route }); ``` In Koa, a context is created for every request that comes to the server and is always referenced as middleware. ```js // request and response as context in Koa app.use(async ctx => { ctx.request; ctx.response; }); ``` ### Side Tip: Installing nodemon Before I start on REST APIs, the core of the article, I want to introduce a great tip that is helpful in building a Node.js application. During the development mode, irrespective of the framework I am using, I use `nodemon` as a dependency to watch for file changes and automatically restart the application. This eliminates the need to run `node [`filename].js` command again and again. You can totally, skip this step and move on to the next one where I show the steps for writing the REST API. This small utility has such an impact on my workflow that it saves hours of development chores. So let me show you how to set it up in our demo application. I am using the same project as previous **Hello World** example to demonstrate this. Write the following command to install it. ```shell npm install -D nodemon ``` `-D` flag is used to tell npm to install the current dependency as a `devDependency`. The difference between it and a normal dependency is that `devDependencies` tend to work only in development environment. They are not installed in a production environment since there is no use of them there. Other types of `devDependencies` you might come across when writing Node applications are linting tools such as ESLint. Once, `nodemon` is installed, append the `package.json` file and an npm script. ```json "scripts":{ "dev": "nodemon app.js" } ``` Point this script to the initial file of the Koa application, in our case, it is `app.js`. To run the application now, all you have to type is `npm run dev` command in your terminal. This time, you will see a few messages suddenly prompted in the terminal. These messages are provided by `nodemon`. ### Building the REST API Finally, you have arrived at the point where you can start building the REST API. You have to install dependencies for the API to work. Let’s install them. ```shell npm i -S koa-body koa-router ``` > What are these dependencies for? **koa-body** is a body-parser middleware. It supports `urlencoded`, multi-part and `json` request bodies. Basically, it helps to create and respond to HTTP `POST` requests which are available as a form field, or a file upload, etc. It tells the server that the incoming request from the client has a body of data. ExpressJS uses the same approach in handling body requests. **koa-router** is the routing middleware that provides ExpressJS style routing using HTTP verbs. It has methods that you can directly use in the application Such as `app.get()`, `app.post()`, etc. **Note:** I will be mocking data in this application for the sake of brevity and a clear understanding of the framework’s concepts. If you want to, you can use the database of your choice. The structure of the data is not complex. Write the following code in the `app.js` file. ```js // app.js const Koa = require('koa'); const koaBody = require('koa-body'); // create app instance const app = new Koa(); // middleware functions app.use(koaBody()); // Require the router here // use the router here app.listen(3000); ``` After body-parser middleware, you are going to have the routes. I am using another file to describe the routes to separate the code for a clear understanding. Create a new file called `books.js`. Define the following inside that file with the data to mock. ```js // books.js const Router = require('koa-router'); // Prefix all routes with: /books const router = new Router({ prefix: '/books' }); let books = [ { id: 101, name: 'Fight Club', author: 'Chuck Palahniuk' }, { id: 102, name: 'Sharp Objects', author: 'Gillian Flynn' }, { id: 103, name: 'Frankenstein', author: 'Mary Shelley' }, { id: 101, name: 'Into The Wild', author: 'John Krakauer' } ]; // Routes will go here module.exports = router; ``` First, I am importing the necessary dependency to create routes: `koa-router`. The next step is to create an instance of this newly imported dependency. Notice the prefix part: `/books`. Using a prefix for the routes is how you can define and categorize different routes. You can also use this technique to classify the different API versions such as `v1`, `v2`, etc. The `books` array is the mock data. It contains information about books and each book is represented by a separate object inside the array. Each object further has three data fields: `id`, `name`, and `author`. Let’s build the first route of our API. ### Creating a route to handle GET request Below is the code for handling a `GET` request in Koa. Add the following code to `books.js`. ```js // books.js router.get('/', (ctx, next) => { ctx.body = books; next(); }); ``` The callback function that is attached to the `router.get()` contains two arguments. I have already explained to you what `ctx` or context is. The last argument is `next()`. This is often used in middleware functions. Any middleware function invokes this function to indicate the current middleware function has suspended running and the next middleware function available must be invoked. This callback function traverses through the `books` array when to send the response. To run this, you have to first include the routes file in `app.js` and invoke them. ```js // app.js const Koa = require('koa'); const koaBody = require('koa-body'); const app = new Koa(); // Set up body parsing middleware app.use(koaBody()); // Require the Router we defined in books.js let books = require('./books.js'); // Use the Router on the sub route /books app.use(books.routes()); app.listen(3000); ``` Next step is to run the command: `npm run dev` and visit the url `http://localhost:3000/books` to see the following result. > _Congratulations! 🎉 You just build your first route using Koa._ Next step is to create a route to fetch a book by its `id`. It is going to use the similar approach as the previous route, plus we see how to extract information from `request.params` object of an incoming request. ```js // books.js router.get('/:id', (ctx, next) => { let getCurrentBook = books.filter(function (book) { if (book.id == ctx.params.id) { return true; } }); if (getCurrentBook.length) { ctx.body = getCurrentBook[0]; } else { ctx.response.status = 404; ctx.body = 'Book Not Found'; } next(); }); ``` Routing parameters are named segments that are used to capture the values specified in the URL. In our case, such as`:id`. Above, we define a routing middleware function that can handle incoming requests from URLs such as `http:localhost:3000/books/103`. Enter this URL in your browser window and you will get the following result. In case of when `id` does not exist or is invalid, you have to send an error message with an HTTP status of `404`. ### Handling a POST request The last route you are going to build for this demonstration is going to handle `POST` requests. ```js // books.js router.post('/new', (ctx, next) => { // Check if any of the data field not empty if ( !ctx.request.body.id || !ctx.request.body.name || !ctx.request.body.author ) { ctx.response.status = 400; ctx.body = 'Please enter the data'; } else { let newBook = books.push({ id: ctx.request.body.id, name: ctx.request.body.name, author: ctx.request.body.author }); ctx.response.status = 201; ctx.body = `New book added with id: ${ctx.request.body.id} & name: ${ctx.request.body.name}`; } next(); }); ``` The `/new` route is used for creating a new book and adding it to our `books` array. I am using this to mock data and not a real database so restarting the application will delete the newly added books. In the above routing middleware, the Koa Context object first checks if any of the data fields required in `request.body` is present or not. If one of them is missing, this routing middleware will be terminated and sends back an error to the user. If everything is fine, this routing middleware accepts the data and returns a success message with the correct HTTP status of code for creating a new record. To run this URL, I am using `curl` command from my terminal but you can use any REST client such as Postman or Insomnia. For our all routes to be more descriptive and follow the REST API pattern, I have re-written every `ctx.body` object from each routing middleware function. Here is how the complete routing file looks so far. ```js // books.js const Router = require('koa-router'); // Prefix all routes with /books const router = new Router({ prefix: '/books' }); let books = [ { id: 101, name: 'Fight Club', author: 'Chuck Palahniuk' }, { id: 102, name: 'Sharp Objects', author: 'Gillian Flynn' }, { id: 103, name: 'Frankenstein', author: 'Mary Shelley' }, { id: 104, name: 'Into The Willd', author: 'Jon Krakauer' } ]; // Routes will go here router.get('/', (ctx, next) => { ctx.body = { status: 'success', message: books }; next(); }); router.get('/:id', (ctx, next) => { let getCurrentBook = books.filter(function(book) { if (book.id == ctx.params.id) { return true; } }); if (getCurrentBook.length) { ctx.body = getCurrentBook[0]; } else { ctx.response.status = 404; ctx.body = { status: 'error!', message: 'Book Not Found with that id!' }; } next(); }); router.post('/new', (ctx, next) => { // Check if any of the data field not empty if ( !ctx.request.body.id || !ctx.request.body.name || !ctx.request.body.author ) { ctx.response.status = 400; ctx.body = { status: 'error', message: 'Please enter the data'; } } else { let newBook = books.push({ id: ctx.request.body.id, name: ctx.request.body.name, author: ctx.request.body.author }); ctx.response.status = 201; ctx.body = { status: 'success', message: `New book added with id: ${ctx.request.body.id} & name: ${ ctx.request.body.name }` }; } next(); }); module.exports = router; ``` This completes the basics of building a REST API using Koa as a Node.js framework. It’s a pretty minimal framework with all the necessary ways to tackle incoming requests and send the response back from the server. Koa also supports ready-made middleware functions to make use of logging, handling errors, testing, compression, and security. > You can find the complete code used in this tutorial at [this Github repository](https://github.com/amandeepmittal/koa-rest-api-tut) --- ## Building offline React Native apps with AsyncStorage Slug: building-offline-react-native-apps-with-asyncstorage ![cover](https://i.imgur.com/5eoYxcI.png) > [Originally published at Heartbeat](https://heartbeat.fritz.ai/building-offline-react-native-apps-with-asyncstorage-dcb4b0657f93) As developers, we love exploring concepts and mechanisms while working with a new framework. React Native as a cross-platform development framework has come quite far in terms of a mature framework since I started playing around with it and then using it for its purpose. Understanding the fundamentals when learning it is something very helpful, and I consider, important. Thus, applying basic fundamentals of React Native knowledge, in this tutorial, I am going to walk you through how to build a todo list application using an offline storage functionality. This storage functionality is provided by a native module in React Native, called `AsyncStorage`. In the journey of building this application, you are going to use a UI component library known as [Native Base](https://docs.nativebase.io/docs/GetStarted.html), which is one of the most popular libraries to build user interfaces among React Native developers. Out of the box, this library speeds up the development process by providing pre-defined UI components that can either be used as they are available or customize them according to our needs. ## What are we building? The outcome from following this tutorial is going to be a complete React Native application that works with realtime offline data from the storage of the device. ## Table of Contents - Prerequisites - Create an Expo app - Exploring AsyncStorage - Utilizing AsyncStorage API - Adding Navigation - Creating a Floating Action Button (FAB) - Navigating between Two Screens - Customize the Header Component - Rendering a list of items using FlatList - Reading Data using AsyncStorage API - Adding a Todolist Item - Deleting a Todolist Item - Mark an Item Check or Uncheck on completion - Passing Data between different screens using the navigation - Display each todo list item - Bonus Section: Adding a Segment - Conclusion ## Prerequisites To follow this tutorial, please make sure you have the following installed on your local development environment and have access to the services mentioned below: - [Node.js](https://nodejs.org/en/) (>=`8.x.x`) with npm/yarn installed. - [expo-cli](https://docs.expo.io/versions/latest/workflow/expo-cli/?) (>=`3.0.4`), previously known as create-react-native-app. It will be best if you use the same exact versions or higher of each utility tool described above. To run and test the React Native application, all you need is an Expo client installed either on your device or an iOS simulator or an Android emulator. Please note that, throughout this tutorial, I will be using an iOS simulator to demonstrate the application. ## Create an Expo app To get started, all you require is to generate a new Expo project. This could be done by opening a terminal window, navigating to a suitable location where you develop projects and running the following commands in the order they are described. ```shell expo init offline-todolist-app # navigate inside the app folder cd offline-todolist-app # install the following dependencies yarn add react-navigation native-base expo-font@5.0.1 lodash.values uuid ``` The last command, as described in the above snippet installs five dependencies that the application is going to use. `yarn` is currently being used as the package manager. You can also use `npm` instead of `yarn`. The use of each dependency will be made clear as throughout this tutorial as they are used. If this is your first time building a React Native application, try not to get overwhelmed by them. ## Exploring AsyncStorage `AsyncStorage` is a simple, asynchronous key-value pair used in React Native applications. It is used for a variety of scenarios but mainly to store data when your app is not using any cloud services, or you want to implement some features in your app that require data storage. It operates globally in a React Native and comes with its own limitations. As a React Native developer, you have to know what these limitations. The first limitation of an `AsyncStorage` API is that the size of the database is set to `6MB` limit. Also, `AsyncStorage` storage is based on SQLite. Thus, it is important to keep [SQLite limitations](https://www.sqlite.org/limits.html) in mind too. Also, it is hard to store complex and nested data structures in form of key-value pairs. Knowing about these limitations, only help you to opt for the persistent solution when developing a mobile app. According to the [React Native's official documentation](https://facebook.github.io/react-native/docs/asyncstorage): > On iOS, AsyncStorage is backed by native code that stores small values in a serialized dictionary and larger values in separate files. On Android, AsyncStorage will use either RocksDB or SQLite based on what is available. ## Utilizing AsyncStorage API Before you dive deep in building the Todolist app, in this section, let us build a small app that saves a value to the `AsyncStorage`, fetches the value from the storage in the client-side React Native app. This will help you how to write basic operations using the storage API. Lastly, you will learn about how to clear the storage completely. Open `App.js` file and add the following snippet. Start by importing the necessary components from React Native API. The most important one here is `AsyncStorage`. After that, define a variable named `STORAGE_KEY`. This variable will be used to store and retrieve the stored data using the `AsyncStorage` API. Think of it as an identifier for the value being stored or name of the key in the key-value pair. Since you are going to store only one value at the moment, there is only the requirement for one key. ```js import React from 'react'; import { StyleSheet, Text, View, TextInput, AsyncStorage, TouchableOpacity } from 'react-native'; const STORAGE_KEY = '@save_name'; ``` Next, let us define an initial state with two empty strings. They are going to be used to save the value of the user input and then retrieve the value to display it on the app screen. After defining the initial state, there is going to be a lifecycle method that is going to load the data when the application starts for the first time or the App component renders. ```js class App extends React.Component { state = { text: '', name: '' }; componentDidMount() { this.retrieveData(); } // ... } ``` In the above snippet, do note that the `App` component is actually a class component and not the default functional component that comes with boilerplate Expo app. Now, there are going to be three methods that will help to store the data, retrieve the data, and clear the app data that is stored. This is going to be done by creating three asynchronous methods. Each of the methods is going to utilize the appropriate API method from `AsyncStorage` API. Every method in the `AsyncStorage` API is a promise-based, hence, let us use `async/await` syntax to follow good practice. ```js retrieveData = async () => { try { const name = await AsyncStorage.getItem(STORAGE_KEY); if (name !== null) { this.setState({ name }); } } catch (e) { alert('Failed to load name.'); } }; ``` In the above snippet, the name of the method implies what they are going to do in the app. The `retrieveData` method is what fetches the data from the storage if it exists. It uses the same identifier that you defined previously, outside the class function component. It utilises the parameter in the state object `name`. Later in the app, you are going to use this parameter to display its stored value. Note that, there is an `if` condition inside this method. This condition says that to fetch the data only when there is a value for the `name` variable exists. This method also uses `try/catch` as they are part and parcel of writing functions with modern `async/await` syntax. Lastly, this method is being invoked inside the lifecycle method. The next function is going to save the data. In the below snippet, you will find that it uses a parameter `name` which on success, is the value that is stored. An alert message will be shown when the input data is saved. ```js save = async name => { try { await AsyncStorage.setItem(STORAGE_KEY, name); alert('Data successfully saved!'); this.setState({ name }); } catch (e) { alert('Failed to save name.'); } }; ``` The last method that you are going to utilize from the `AsyncStorage` API is called `clear()`. This deletes everything that is previously saved. It is not recommended to use this method directly if you want to delete only a specific item from the storage. For that, there are methods like `removeItem` or `multiRemove` available by the API. You can read more about them in the official documentation [here](https://facebook.github.io/react-native/docs/asyncstorage#clear) or later when building the Todolist application. ```js removeEverything = async () => { try { await AsyncStorage.clear(); alert('Storage successfully cleared!'); } catch (e) { alert('Failed to clear the async storage.'); } }; ``` This snippet will throw an `Alert` box on the device screen when everything is cleared from the storage. The last two methods are going to be used to create a controlled input. ```js onChangeText = text => this.setState({ text }); onSubmitEditing = () => { const onSave = this.save; const { text } = this.state; if (!text) return; onSave(text); this.setState({ text: '' }); }; ``` After that, add the code snippet for the `render` method, followed by the styles for each UI component. Lastly, do not forget to export `App` component for it to run on the simulator or the real device. ```js render() { const { text, name } = this.state return ( Hello {name}! Clear Storage ) } } // class component App ends here const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', alignItems: 'center', justifyContent: 'center' }, text: { fontSize: 20, padding: 10, backgroundColor: '#00ADCF' }, input: { padding: 15, height: 50, borderBottomWidth: 1, borderBottomColor: '#333', margin: 10 }, button: { margin: 10, padding: 10, backgroundColor: '#FF851B' }, buttonText: { fontSize: 14, color: '#fff' } }) export default App ``` Now to run the application, go to the terminal window and execute the command `expo start`. After that, you will see the following screen on the simulator. Since there is no data stored right now, the text after the word `Hello` is empty. Use the input field to save a string or a name or anything and then press the enter key. You will get the following output. Whatever input you entered, it will be displayed next to the word `Hello`. Even if you refresh the Expo client, the value stored does not go away. Only when pressing the button below `Hello` statement that says `Clear Storage` is the way to delete the stored value. Refresh the Expo client after you clear the storage to get the following output. This complete the section where you learned about how to utilize `AsyncStorage` API to save and fetch the data. From the next section onwards, you will be building the Todolist application. ## Organizing the application Since a React Native application was already generated in the previous step, you can continue to utilize that app by modifying everything inside the `App.js` file. Or create a new one if it serves you well. You have already installed the necessary npm modules. This is the time to start utilizing them in order to build the offline todo list app. Before beginning with the development of the app, create the following folders and files inside them. This will give a structure to manage the app later or if you want to extend by adding new features to it. From the structure, notice that there are three new folders being created. This structure is the separation of concerns between the different aspect of a mobile app. Such as files or configuration related to navigation should be separated from the screens. The above structure is also a common pattern that many React Native developers have started to follow in their work. ## Adding Navigation Inside the `navigation` folder, there is an `index.js` file that is going to hold all the configuration there is to be defined. The reason `react-navigation` module is used is to create a stack navigator that allows the user to visit the two screens the following application has. The navigation mode is going to be `modal`. Yes, you can utilize `pre-defined` navigation modes or animation patterns. Let us start by importing the necessary components inside the `index.js` file. ```js import React from 'react'; import { createStackNavigator, createAppContainer } from 'react-navigation'; import HomeScreen from '../screens/HomeScreen'; import AddTaskScreen from '../screens/AddTaskScreen'; ``` From the above snippet, notice that the `createStackNavigator` is a function that returns a React component. It takes a route configuration object. The `createAppContainer` is responsible for linking the current React Native app while maintaining the navigation state from the top-level component. The top-level component in your app is `App`. With the help of `createAppContainer`, you are going to create a provider and wrap the `App` component inside it. This will benefit the entire application as every screen or component defined is going to have a navigation state. You will learn some of the many benefits provided by the navigation state later. Lastly, in the above snippet, there are going to be a screen component. These screen components are going to hold the business logic necessary to run the todo list app. You can think of them as containers. Right now, the route configuration object is going to be as the following snippet. ```js const StackNav = createStackNavigator( { Home: { screen: HomeScreen }, AddTask: { screen: AddTaskScreen } }, { mode: 'modal' } ); ``` The `mode` is important to specify here. It defines the style for rendering the next screen component. In the above case, it is `AddTask` screen. In an iOS or Android app, the default transition is always a `card`. You are changing this default transition by specifying the `mode` property and setting its value to `modal`. The `modal` pattern Make the screens slide in from the bottom, which is a common iOS pattern. Only works on iOS but has no effect on Android. Lastly, you have to export the app container that utilizes the `StackNav`. Here is the code for that. ```js const RootNavigator = createAppContainer(StackNav); export default RootNavigator; ``` Now, open `App.js` file and add the following content. ```js import React from 'react'; import RootNavigator from './navigation'; export default function App() { return ; } ``` Before running the app, make sure there is a mock component to render inside the files `HomeScreen.js` and `AddTaskScreen.js`. Otherwise, it will throw an error. You can add the dummy component for now. ```js // HomeScreen.js import React, { Component } from 'react'; import { Text, View } from 'react-native'; export class HomeScreen extends Component { render() { return ( Offline Todolist App ); } } export default HomeScreen; // AddTaskScreen.js import React, { Component } from 'react'; import { Text, View } from 'react-native'; export class AddTaskScreen extends Component { render() { return ( Add Task Screen ); } } export default AddTaskScreen; ``` Now run the app using `expo start` command, and you will get the following result. This completes the navigation section. ## Create a Floating button Inside the `components/FloatingButton.js` file, you are going to create a floating action button or in mobile development, commonly known as FABs. These type of buttons are often distinguished by a circled icon floating above the UI in a fixed position. If you are an Android user or have seen a mobile app following any material design specification, you might have noticed them. In the current app, this `FloatingButton` is going to be responsible for navigating from the `HomeScreen` to the `AddTaskScreen`. Since it is going to be a presentation component, you should define it as a functional component that accepts only one prop. This prop `actionOnPress` is going to be a method defined inside the `HomeScreen.js` file that will contain the logic of navigating between the two screens later. One important thing to notice in the snippet below is that the component library `native-base` is being used to create the FAB button. It saves a good amount of time and lines of code to create and style a component like below. ```js import React from 'react'; import { StyleSheet } from 'react-native'; import { Icon, Fab } from 'native-base'; const FloatingButton = ({ actionOnPress }) => ( ); const styles = StyleSheet.create({ button: { backgroundColor: '#5859f2' } }); export default FloatingButton; ``` ## Navigating Between Two Screens Once you have defined it, go to the file `HomeScreen.js` and the following snippet of code. ```js import React, { Component } from 'react'; import { View, Text, StyleSheet } from 'react-native'; import { AppLoading } from 'expo'; import * as Font from 'expo-font'; import FloatingButton from '../components/FloatingButton'; export class HomeScreen extends Component { state = { isDataReady: false }; componentDidMount = () => { this.loadFonts(); }; loadFonts = async () => { try { await Font.loadAsync({ Roboto: require('../node_modules/native-base/Fonts/Roboto.ttf'), Roboto_medium: require('../node_modules/native-base/Fonts/Roboto_medium.ttf'), Ionicons: require('../node_modules/native-base/Fonts/Ionicons.ttf') }); this.setState({ isDataReady: true }); } catch (err) { alert('Application Error. Cannot load fonts.'); } }; onPressFab = () => { this.props.navigation.navigate('AddTask'); }; render() { const { isDataReady } = this.state; if (!isDataReady) { return ; } return ( Home Screen ); } } const styles = StyleSheet.create({ container: { flex: 1 } }); export default HomeScreen; ``` In the above snippet, the first and important thing to notice is the `loadFonts` method. This asynchronous method is a requirement to make Native Base UI library to work in any React Native, and Expo generated application. NativeBase use some custom fonts that are loaded using `Font.loadAsync` function. This function is provided by the expo module `expo-font` which allows you to use any fonts or icons in React Native components. The `AppLoading` method is a React component that tells Expo to keep the app loading screen visible until `Font.loadAsync()` the method has run successfully. In general, this a useful method to utilize when your app is using custom fonts, logos, icons, and so on. In the current application, you are going to utilize this React component again when fetching data from `AsyncStorage` API (_that you will see in action later in this tutorial_). The `AppLoading` will only stop running when the boolean value for the state variable `isDataReady` is set to true. This boolean value is only set to true when `Font.loadAsync()` has finished running. Once the application has loaded all necessary fonts and icons, you will get the following result. From the above snippet, take a look at the method `onPressFab` which is being passed to the `FloatingButton` component as the prop `actionOnPress`. This function utilizes a navigation method provided called `navigation.navigate()` with the value of the screen being passed as the argument: `AddTask`. Do note that, the value of the argument being passed should be the exact name of the screen defined earlier when configuring `StackNavigator`. Click on the button, and you will be directed to the next screen. Did you notice the `back` button on the `AddTaskScreen`? This is again where `react-navigation` comes in handy. While working on a real-time React Native application, you often want to use the `react-navigation` library if it suits your requirements. It provides simple solutions out of the box. ## Customize the Header Component With Native Base components library, it is easy to customize a header component in few lines of code. Inside the file `Header.js` add the following snippet. Again, this is a functional component since it is going to enhance the UI and is not running business logic. ```js import React from 'react'; import { Header as NBHeader, Body, Title } from 'native-base'; const Header = () => { return ( Header ); }; export default Header; ``` The `Header` component from the `native-base` library takes a `Body` as an input. The body can further contain the rendering logic to modify the existing default `Header` component from the native base library itself. You can use inline styles or even `StyleSheet` object from `react-native` to customize the `Header` component as above, or any other native base UI component in general. Take a look at the `backgroundColor` and the `color` to the `Title`. `Title` is where the text to be displayed on this component goes. Import this component inside the `HomeScreen.js` file. Also, import the `StatusBar` component from the `react-native`. Since the background of the `Header` component is going to be a customize blue color, it is better to change the default dark `StatusBar` style into something pleasing and light. ```js import { View, Text, StyleSheet, StatusBar } from 'react-native'; import Header from '../components/Header'; ``` Inside the class component, the first thing you have to do is hide the header that is being provided by the stack navigator from `react-navigation` library. The object `navigationOptions` is how to customize the default navigators that `react-navigation` renders. ```js static navigationOptions = { header: null } ``` Next, inside the `render()` method add the following before the omnipresent `Text` component. ```js
Home Screen ``` The rest of the code inside the `HomeScreen.js` file remains unchanged. The `StatusBar` is modified by defining the a value using its pre-defined prop `barStyle`. When using a Header component from Native Base UI library, the `StatusBar` from React Native comes after you define the JSX code for the header. Notice this in the above snippet. This is how it works with Native Base library. The following screen is what you get as the result of the above snippets. ## Rendering a list of items using FlatList In this section, you are going to set up a List component that accepts mock or dummy data from an array defined as a property to the initial state. Open `HomeScreen.js` file and modify the state for now. ```js state = { isDataReady: false, mockItems: ['First Item', 'Second Item', 'Third Item'] }; ``` _Why dummy data?_ Later when you are going to hook `AsyncStorage` API to save and fetch the data from the database, in other words, playing around with real-time data operations, there are going to be separate methods that are going to handle each of the data operations. For now, let us hook up the business logic to display a list of items as well as the ability to add a new item using the modal screen you have set up in the previous steps. The `FlatList` component is the ideal way to display a list of items in a React Native application. It is a cross-platform component, and by default a vertical way to display a list of data items. It requires two props: `data` and `renderItem`. The `data` is the source of information for the list in the form of an array. The `renderItem` takes one item from the source, iterates over them, and returns a formatted component to render those items. Styles that can be applied to a FlatList component is done by the prop `contentContainerStyle` that accepts the value of Stylesheet object. The reason to use `FlatList` is that it is performance effective. Of course, you can use `ScrollView` but it renders items from memory, which is not a very performant effective way to display a lengthy list of items. `ScrollView` is a wrapper on the View component that provides the user interface for scrollable lists inside a React Native app. In the file `HomeScreen.js` replace the `Text` component with following `FlatList` and do not forget to import it and custom presentational component `Item` that is going to display each item in the list. ```js // import statements import { View, FlatList, StyleSheet, StatusBar } from 'react-native'; import Item from '../components/Item'; // in render method, replace with the following { return ; }} keyExtractor={item => item.id} />; ``` Now open the file `components/Item.js` and add the following snippet. ```js import React from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native'; const { width } = Dimensions.get('window'); const Item = ({ text }) => { return ( {text} ); }; const styles = StyleSheet.create({ container: { borderBottomColor: '#5859f2', borderBottomWidth: StyleSheet.hairlineWidth, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, rowContainer: { flexDirection: 'row', width: width / 2, alignItems: 'center' }, text: { color: '#4F50DC', fontSize: 18, marginVertical: 20, paddingLeft: 10 } }); export default Item; ``` Another new React Native component to notice in the above snippet is `Dimensions`. It helps to set the initial `width` and `height` of a component before the application runs. We are using its `get()` method to acquire the current device's width and height. In the simulator, you will get the following result. ## Reading Data using AsyncStorage API In this section, you are going to add all methods that will contain business logic to save and fetch the data from the `AsyncStorage`. This logic will be composed of three operations: - add a todolist item - fetch all items to display - delete an item from the list - also, check the state of each list item whether it is marked as completed or not These operations are going to communicate with the realtime data on the device. You are going to use objects instead of an array to store these items. `AsyncStorage` operates on key-value pairs and not arrays. Each object is going to be identified through a unique ID. In order to generate unique IDs, you are going to use a module called `uuid` which was installed earlier. The structure of each todo item is going to be like this: ```js 45745c60-7b1a-11e8-9c9c-2d42b21b1a3e: { id: 45745c60-7b1a-11e8-9c9c-2d42b21b1a3e, // same id as the object textValue: 'New item', // name of the ToDo item isCompleted: false, // by default, mark the item unchecked createdAt: Date.now() } ``` But if you are going to use Objects instead of an array, how are you going to iterate over each item in the object? `FlatList` component only takes an array to iterate. Well, do you remember installing a utility package called `lodash.values`? That package is going to be really helpful in converting the object into an array. First, let us start by importing all components and custom components required in order to build the application inside `HomeScreen.js` file. ```js import React, { Component } from 'react'; import { FlatList, View, StatusBar, StyleSheet, AsyncStorage } from 'react-native'; import uuidv1 from 'uuid/v1'; import _values from 'lodash.values'; import { Button, Text as NBText } from 'native-base'; import { AppLoading } from 'expo'; import * as Font from 'expo-font'; import Header from '../components/Header'; import Item from '../components/Item'; import FloatingButton from '../components/FloatingButton'; ``` After writing these import statements, let us modify the initial state. ```js state = { todos: {}, isDataReady: false }; ``` From the above snippet, do take a note that the dummy array of data is replaced by the object `todos`. Next, you are going to write an asynchronous method to load the todos items from the object that is stored using `AsyncStorage` API. Also, let us merge the previous asynchronous method to load all the fonts with this method, such as the value of the initial state `isDataReady` is set to the boolean `true` only once. You will also have to modify the contents of the lifecycle method. ```js componentDidMount = () => { this.loadTodos(); }; loadTodos = async () => { try { await Font.loadAsync({ Roboto: require('../node_modules/native-base/Fonts/Roboto.ttf'), Roboto_medium: require('../node_modules/native-base/Fonts/Roboto_medium.ttf') }); const getTodos = await AsyncStorage.getItem('todos'); const parsedTodos = JSON.parse(getTodos); this.setState({ isDataReady: true, todos: parsedTodos || {} }); } catch (err) { alert('Application Error. Cannot load data.'); } }; ``` `AsyncStorage.getItem()` reads anything saved on the device database. It is essential to parse the data incoming from the storage into JSON. If you are not parsing the data, the application is going to crash. When setting the state in the above snippet, the `todos` object is getting the default value of an empty object is there is no data from the storage. This is also an essential step to perform and keep in mind for other use cases with similar scenarios. ## Adding a Todolist Item Now, let us add the second method `addTodo` that is actually going to add the new item in the storage. The method defines before `addTodo` is actually storing the items in the storage. Again, you are using `JSON.stringify()` since AsyncStorage requires the data to be a string inside the single object. So when saving the item if you are not using `JSON.stringify()` your app is going to crash. The `AsyncStorage.setItem()`is the function from the API that is similar to any key-value paired database. It takes the first argument, `todos` in the snippet below. This argument value is going to be the name of the store. The parameter `newTask` passed to the `addTodo` function is going to be the object. Using `if` statement, there is a check whether the todo item being entered is not empty. `this.setState` uses a callback method that has access to `prevState` object. It gives any todo item that has been previously added to the list. Inside the callback, you are first creating a new ID using `uuidv1` method. Then create an object called `newTodoObject` which uses the ID as a variable for the name. This object represents each item in the todo list. Further, create a new object called `newState` which uses the `prevState` object, and finally adds `newTodoObject` object in todoliist of items. It might sound overwhelming since a lot is going on but try implementing the code, you will understand it better. ```js saveTodos = newToDos => { const saveTodos = AsyncStorage.setItem('todos', JSON.stringify(newToDos)); }; addTodo = newTask => { const newTodoItem = newTask; if (newTodoItem !== '') { this.setState(prevState => { const ID = uuidv1(); const newToDoObject = { [ID]: { id: ID, isCompleted: false, textValue: newTodoItem, createdAt: Date.now() } }; const newState = { ...prevState, todos: { ...prevState.todos, ...newToDoObject } }; this.saveTodos(newState.todos); return { ...newState }; }); } }; ``` ## Deleting a Todolist Item Similar to the `addTodo` method, you are going to add another method called `deleteTodo`. This will take care of removing an individual item from the list on the basis of `id` of that item object. Since you are using the `id` of the object both to identify the object inside the bigger object `todos` and assign each individual object the same `id`, the following code saves a lot of time. At last, using the `saveTodos` method, the storage is being updated with a remaining number of items. ```js deleteTodo = id => { this.setState(prevState => { const todos = prevState.todos; delete todos[id]; const newState = { ...prevState, ...todos }; this.saveTodos(newState.todos); return { ...newState }; }); }; ``` ## Mark a Todo Item Check or Uncheck on completion The last two methods that are going to take care of whether each individual item is checked or not are going to be represented by `inCompleteTodo` and `completeTodo` methods. Both of these methods are going track which items in the to-do list have been marked completed by the user or have been unmarked. They are going to act as a toggle and only update the value of `isCompleted` instead rather updating the whole todo list item object. This is again, possible because of a unique `id` for each object. Again in the last, before each of the methods returns the new state, using the `saveTodos` method, the storage gets an update. ```js inCompleteTodo = id => { this.setState(prevState => { const newState = { ...prevState, todos: { ...prevState.todos, [id]: { ...prevState.todos[id], isCompleted: false } } }; this.saveTodos(newState.todos); return { ...newState }; }); }; completeTodo = id => { this.setState(prevState => { const newState = { ...prevState, todos: { ...prevState.todos, [id]: { ...prevState.todos[id], isCompleted: true } } }; this.saveTodos(newState.todos); return { ...newState }; }); }; ``` ## Passing Data between different screens using the navigation In this section, you are going to edit each render method that is responsible for displaying the interface for the operations you defined in the previous sections, to happen in realtime. Let us start by editing `onPressFab` method inside the `HomeScreen.js`. This method right navigates to the `AddTaskScreen`. By passing an object with to add a new item to the list (_hence, pass the method addTodo_) you are going to utilize another advantage that a sleek library `react-navigation` provides. That is, to pass data between different screens. First, edit the `onPressFab` method like the below snippet. ```js onPressFab = () => { this.props.navigation.navigate('AddTask', { saveItem: this.addTodo }); }; ``` Next, open `AddTaskScreen.js` and add the following snippet. ```js import React, { Component } from 'react'; import { View } from 'react-native'; import { Form, Item, Input, Button, Text as NBText } from 'native-base'; export class AddTaskScreen extends Component { state = { text: '' }; onChangeText = event => { this.setState({ task: event.nativeEvent.text }); }; onAddTask = () => { this.props.navigation.state.params.saveItem(this.state.task); this.props.navigation.goBack(); }; render() { return (
); } } export default AddTaskScreen; ``` The snippet above uses the native base library to create a controlled input form to let the user add a new item to the todo list. Next, it has a button to add the item. Since the `Input` component from Native Base is based on the React Native's `TextInput`, you can use all the props that are available to `TextInput`. Also, take a note that, to create an input field when using Native base as the UI library, the `Input` component has to be wrapped by an `Item` which is further wrapped inside `Form` element. Here is a brief overview of the props used in the above snippet. - **value**: the value of the text input. By default, it will be an empty string since we are using the local state to set it. As the state updates, the value of the text input updates. - **placeholder**: just like in HTML, a placeholder is to define a default message in the input field indicating as if what is expected. - **onChange**: is a callback that is called when the text input's text changes. Changed text is passed as an argument to the callback handler `onChangeText`. This handler accepts the text value from `event.nativeEvent`. - **clearButtonMode**: a clear button should appear on the right side of the text view. The default value is `never` that you are modifying to `always` in the above component. - **returnKeyType**: determines how the return key on the device's keyboard should look. You can find more values or platform-specific values here. Some of the values are specific to each platform. - **autoCorrect**: this prop let us decide whether to show the autocorrect bar along with keyboard or not. In the current case, you have set it to false. - **onSubmitEditing**: contains the business the logic in the form of a callback as to what to do when the return key or input's submit button is pressed. We will be defining this callback in Main.js. Lastly, take a look at the method `onAddTask` which uses navigation state to save the text value of the todo item. After use presses the button or the handler `onSubmitEditing` triggers, it is going to further run the method `addTodo` from `HomeScreen` and navigate back to the `HomeScreen` itself, using the navigation props method `goBack()`. On Clicking the Fab button, you get the following screen. ## Display each todo list item To display each todo list item, you will have first to pass the props as shown below using the `renderItem` in the `FlatList`. ```js ``` Next, go to `Item.js` file and add the following snippet. ```js import React from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Dimensions } from 'react-native'; import { Icon } from 'native-base'; const { width } = Dimensions.get('window'); const Item = ({ inCompleteTodo, completeTodo, textValue, id, deleteTodo, isCompleted }) => { toggleItem = () => { if (isCompleted) { inCompleteTodo(id); } else { completeTodo(id); } }; return ( {textValue} deleteTodo(id)}> ); }; const styles = StyleSheet.create({ container: { borderBottomColor: '#5859f2', borderBottomWidth: StyleSheet.hairlineWidth, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, text: { color: '#4F50DC', fontSize: 18, marginVertical: 20, paddingLeft: 10 }, rowContainer: { flexDirection: 'row', width: width / 2, alignItems: 'center' } }); export default Item; ``` In the above snippet, the key points to note are, using Native Base, you can use the `Icon` component (_since you are already loading the Ionicons library in the parent component asynchronously_). Next, the props `Item` components receive are to toggle an item's state of whether it is complete or not, display the text value of the item and lastly, a button to delete the item itself. Save the component file, hop back on the simulator file, and try adding one or many items in this list. See everything works. Even on refreshing the app, and the items do not disappear. ## Bonus Section: Adding a Segment In this section, you are going to separate the UI for managing the completed list of items and items that are pending to be done. To provide this feature, you are going to use Native Base library solely. Keeping the data source same from the storage API, let modify the state by adding one more property. Open `HomeScreen.js` file and add the following. ```js // add "filter" to existing the state state = { todos: {}, isDataReady: false, filter: 'Todo' }; ``` The value of the `filter` is going to be `Todo` by default. This means it is going to show the pending todo list items as the home screen to the user. Next, you are going to add another handler function called `filteredItems`. This method will evaluate the value of the state and filter the values from the `todos` to match the state. Again, to use JavaScript filter method, you are going to convert `todos` object using lodash method `_values`. ```js filteredItems = () => { if (this.state.filter === 'Todo') { return _values(this.state.todos).filter(i => { return !i.isCompleted; }); } if (this.state.filter === 'Complete') { return _values(this.state.todos).filter(i => { return i.isCompleted; }); } return this.state.todos; }; ``` Next, let us modify the render method to achieve the desired result. Inside the render method, you are going to add a new UI element from Native base called `Segment`. This is going to display two buttons, each of which can be activated when pressed. The activation of each this button depends on the value of the state property `filter`. ```js // import Segment from Native Base import { Button, Text as NBText, Segment } from 'native-base' // inside the render method... const { isDataReady, filter } = this.state // just before flatlist add a new view // styles corresponding to the new View element contentHeader: { alignItems: 'center', justifyContent: 'center' } ``` Lastly, change the value of the `data` prop on `FlatList` and set it to the item returned from the method `filteredItems()`. ```js ``` You will get the following result. ## Conclusion _Congratulations!_ You have just learned how to build an offline mobile application using latest tech stack and libraries like React Native, Expo, and Native Base component UI. You have learned many key points in this tutorial, and I hope you enjoyed following it, and reading it. Use the knowledge you have gained in this tutorial in a realtime application and show it to your peers. The possibilities to enhance this application or the use the knowledge is endless. --- ## Bypass CORS to fetch files when working with localhost Slug: bypass-cors-when-working-with-localhost Sometime back I learned about [opening files or directories using the `open` command from the CLI on a Mac](/blog/how-to-open-any-folder-from-terminal-in-finder-on-mac/). It's simple yet effective. Another use case that I've been using it for is to open the current project when in `localhost` in the browser and bypass the CORS policy to fetch files that are also available locally. ```shell open -a "Google Chrome Canary" --args --user-data-dir="/tmp/chrome_dev_test" --disable-web-security ``` The argument passed in the above command overrides the browser's default behavior. --- ## Change the color of hidden files and folders in VS Code Slug: change-color-of-hidden-files-folders-in-vscode > **Last update:** 17 December, 2023 I often switch between dark and light themes in VS Code to keep my coding environment fresh and engaging. My preference leans towards [light themes](https://amanhimself.dev/blog/setup-macbook-m1/#themes), such as pre-installed Quiet Light. ## Customizing the morgan.code theme My latest choice is the [morgan.code theme](https://marketplace.visualstudio.com/items?itemName=morgan-codes.morgan-codes-vscode-theme), crafted by [Morgan Richardson](https://www.instagram.com/morgan.codes/). Its contrasting colors are particularly soothing for my eyes. After using this theme for some time, I noticed a longing for a familiar sight &mdhash; files and folders ignored by git displayed in a specific shade of gray. The **morgan.code** theme, however, presents these items in a blue-ish tone, possibly Cyan or Aqua. ![Original theme](https://i.imgur.com/J6hik7g.jpg) ## Changing ignored files and folders color VS Code has a property named `gitDecoration.ignoredResourceForeground` for customizing the color of ignored files and folders. This property when used in conjunction with [`workbench.colorCustomizations`](https://code.visualstudio.com/api/references/theme-color) allows overriding the default theme color. Here is how I adjusted the color setting in my `settings.json`: ```json { // ... "workbench.colorCustomizations": { // select theme you want to apply color customization value "[morgan.codes]": { "gitDecoration.ignoredResourceForeground": "#434343" } } } ``` With this simple tweak, the ignored files and folders now appear in a familiar gray. My optic nerves are happy again. ![customized theme](https://i.imgur.com/sk0tFYi.jpg) ## Conclusion The level of customization in VS Code allows, never ceases to amaze me. --- ## Change comment color visibility in a VS Code theme Slug: change-comment-color-visibility-in-a-vs-code-theme Switching to a different theme in VS Code can often lead to a mismatch in personal preferences. I enjoy personalizing themes with subtle modifications, especially when I find one theme that suits my taste. I recently started using [Digi-Angler Dark theme](https://marketplace.visualstudio.com/items?itemName=Digi-Angler.digi-angler-dark-theme), a variant of the renowned [Dracula color scheme](https://draculatheme.com/). Returning to a dark theme after a while felt like familiar territory, reminiscent of my years using the Dracula theme in VS Code. ## The issue with the comment color Using Digi-Angler, one thing that is a bit too much for me is the color value used for code comments. I prefer comment colors that blend into the background, a preference shaped by my experiences across various code editors, terminal apps, and even while reading code on documentation sites. The sharp, eye-catching color used for comments in this theme didn't sit well with me. ## Customizing comment color in VS Code To address this, I stumbled upon `editor.tokenColorCustomizations` in VS Code. It is a feature that allows altering specific color values in the active theme. You add this property to your editor's `settings.json` file and specify the scope for the desired change. ## Using textMateRules for Token Customization VS Code's tokenization engine is based on [TextMate grammars](https://macromates.com/manual/en/language_grammars), and the customization is done within `textMateRules`. Here's how you can change the comment color: ```json { "editor.tokenColorCustomizations": { "textMateRules": [ { "scope": "comment", "settings": { "foreground": "#9c9c9c" } } ] } } ``` The above code snippet applies the comment color `#9c9c9c` to all themes you use inside VS Code. It also means when you switch from one theme to another, this comment will remain consistent. ## Theme specific customization To tweak the token value for a particular theme, wrap `textMateRules` with the theme name. The following examples demonstrate defining the name of the `[theme]` only applies the comment color `#9c9c9c` for that theme. ```json { "editor.tokenColorCustomizations": { "[Digi-Angler Dark Theme]": { "textMateRules": [ { "scope": "comment", "settings": { "foreground": "#9c9c9c" } } ] } } } ``` ## Conclusion VS Code's flexibility in customization is a significant advantage. It allows you to tailor any theme to your liking. To learn more about syntax highlighting, tokens, and scopes, see [VS Code documentation](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide#textmate-tokens-and-scopes). --- ## Change cursor color in VS Code to use a linear gradient Slug: change-cursor-color-in-vscode > **Note**: With the latest VS Code version, it seems the options to tweak cursor has been disabled, so the following the strategy will not work. Until yesterday, I was unaware that I could change the cursor color in VS Code. Not only just change the color but also use a linear gradient. All thanks to VS Code's customizability. It gets this behavior from the Monaco editor. You can find extensive [documentation and a playground](https://microsoft.github.io/monaco-editor/) for Monaco Editor's API. ## Install APC Customize UI++ The extension [APC Customize UI++](https://marketplace.visualstudio.com/items?itemName=drcika.apc-extension) allows customizations that are beyond VS Code's abilities. Mostly because VS Code is an electron app and like any other electron app, uses CSS and JS. After [installing the extension](https://marketplace.visualstudio.com/items?itemName=drcika.apc-extension), open command palette in VS Code and then run: Enable APC extension. This will enable this extension and any custom settings you're going to apply in the next session, VS Code will automatically ask to restart the editor. ## Find a gradient Find a gradient combination. For my use case, I used [webgradients.com](https://webgradients.com/) which has a collection of free 180 linear gradients. ## Customize the color of the cursor To use APC extension, open `settings.json` file in the VS Code editor. Then, create a block to define the additional stylesheet definitions. ```json { "apc.stylesheet": { ".monaco-editor .cursor": } } ``` The `.monaco-editor .cursor` accepts a CSS value. You can set the `linear-gradient()` function value on the `background` property: ```json { "apc.stylesheet": { ".monaco-editor .cursor": "background: linear-gradient(to bottom, #FF8F00 0%, #FF204E 100%);" } } ``` That's it! On restarting the VS Code editor, the blinking cursor has the linear gradient value is applied to it. ![Linear gradient applied to cursor in VS Code.](/images/change-cursor-color-vscode.png) --- ## Change PICO-8 cart storage location on macOS Slug: change-pico-8-cart-storage-location PICO-8 stores its configuration and cartridges on macOS by default in the user Library directory: ```bash /Users/Username/Library/Application Support/pico-8/carts/ ``` While this location might work for some enthusiasts and game developers, I prefer to keep all of my hobby project's source code and saved files in a single directory. This approach makes it easier to manage backups without searching in multiple locations on my macOS. **This default directory path is also defined in the PICO-8 configuration file** (`pico-8/config.txt`) under the `-root-path` parameter: ```plain // Location of pico-8's root folder root_path /Users/Username/Library/Application Support/pico-8/carts/ ``` To change this location, you can edit the PICO-8 configuration file to point to your desired directory for storing cartridges. For example, I change it to: ```plain root_path /Users/Username/Documents/Code/pico-8-carts/ ``` Before making this change, close the PICO-8 app before editing the configuration file. Additionally, you must have opened the PICO-8 app at least once. Otherwise, there will be no `config.txt` file created if you have only copied the `PICO-8.app` to your `Applications` directory without launching it. Finally, backing up your cartridges before changing the storage locations is important. --- ## Chat app with React Native (Part 1) - Build reusable UI form elements using react-native-paper Slug: chat-app-with-react-native-part-1 ![cover](https://i.imgur.com/ROYjoYo.jpg) This year, the React Native community has seen a lot of changes. Starting from community adaption of React Hooks, the official documentation having [new domain](http://reactnative.dev/), one of the most popular library `react-navigation` adopting a more dynamic and component-based approach to add routes to your apps and lastly, `react-native-firebase` the go-to package to use Firebase SDK, released its sixth version with some improvements. In this tutorial series, I am going to use all of the latest version packages described previously to showcase how to build an app with React Native in 2020. You are going to learn a lot about these libraries while following along as well as build a chat app. The purpose of this tutorial is to get you familiar with all the latest updates in React Native world and its libraries such as `react-navigation` and `react-native-firebase` that are often the choice. If you wish to add a new feature that is not covered in this tutorial, feel free to do that and follow along at your own pace. ## Requirements The following requirements are going to make sure you have a suitable development environment: - Node.js above `10.x.x` installed on your local machine - JavaScript/ES6 basics - watchman the file watcher installed - `react-native-cli` installed through npm or access via npx - [`react-navigation`](https://reactnavigation.org/docs/getting-started) - [`Firebase`](http://console.firebase.google.com/) project - [`react-native-firebase`](https://rnfirebase.io/) - [`react-native-paper`](https://reactnativepaper.com/) For a complete walkthrough on how you can set up a development environment for React Native, you can go through official documentation here. Also, do note that the following tutorial is going to use the react-native version `0.61.5`. Please make sure you are using a version of React Native above `0.60.x`. ## Installing libraries To begin, start by creating a new React Native project and installing libraries as described in the following steps. You are going to need to open a terminal window for this process. ```shell npx react-native init ChatApp # navigate inside the project directory cd ChatApp # install following libraries for navigationOptions yarn add @react-navigation/native @react-navigation/stack react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view react-native-paper react-native-vector-icons ``` After installing the dependencies, please make sure to follow instructions given in their official documentation to configure their native binaries to make it work with React Native. - [`react-native-paper`](https://callstack.github.io/react-native-paper/getting-started.html) - [`react-navigation`](https://reactnavigation.org/docs/getting-started) These instructions may change in the future, so it is better to follow the official documentation. iOS users, make sure to install pods via [cocoapods](https://cocoapods.org/) where ever necessary. ## Creating reusable form elements In this section, let us create some reusable form components such as `FormInput` and `FormButton`. These UI components are going to be used in two screens: Login and Signup. The advantage these reusable form components are going to give is that you do not have to write the same common code again and again for both screen components. At the root of this React Native app, create a new directory called `src/` and inside it create a new directory called `components/`. Inside this directory, create a new file called `FormInput.js`. This component is going to provide a Text Input field for the screen components to use and for the user to enter the credentials. Start by importing the following statements. ```js import React from 'react'; import { StyleSheet, Dimensions } from 'react-native'; import { TextInput } from 'react-native-paper'; ``` [`Dimensions`](https://reactnative.dev/docs/dimensions) from React Native core API, provides a way to get the screen width and height. Instead of giving the fix width and height to a text input field, let this API calculate it for us. You can get the application's screen and height by adding the following snippet. ```js const { width, height } = Dimensions.get('screen'); ``` Next, export the default function `FormInput` that is going to have some props. ```js export default function FormInput({ labelName, ...rest }) { return ( ); } ``` The `...rest` props must be the last prop passed as a parameter, otherwise, you are going to get an error. The purpose of passing this prop is to allow the component to have other props value. Lastly, define the corresponding styles for this reusable component. ```js const styles = StyleSheet.create({ input: { marginTop: 10, marginBottom: 10, width: width / 1.5, height: height / 15 } }); ``` The next reusable component is going to be in a separate file called `FormButton.js`. It is similar to `FormInput` except that this component is going to be used to display a button on the screen. It is also going to use the width and height of the screen using `Dimensions` from React Native. Here is the complete code snippet: ```js import React from 'react'; import { StyleSheet, Dimensions, Text } from 'react-native'; import { Button } from 'react-native-paper'; const { width, height } = Dimensions.get('screen'); export default function FormButton({ title, modeValue, ...rest }) { return ( ); } const styles = StyleSheet.create({ button: { marginTop: 10 }, buttonContainer: { width: width / 2, height: height / 15 } }); ``` The `react-native-paper` UI library, has three modes to display a button. - `text`: a flat button without background or outline - `outlined`: a button with the outline - `contained`: a button with background color and elevation shadow For different purposes, you are going to make use of different button modes. You will see them in screen components later. That is why it is better to accept the value as a prop (_as mentioned in the above snippet: `modeValue`_). ## Create a login screen To being implementing screens in the current app, start by creating the most essential screen called `LoginScreen`. This is going to be the initial route when the user is not authenticated or authorized to enter the app and use its features. Here is a demo of the screen you are going to achieve in this section. Inside `src/`, create another directory called `screens/`. In this directory, we are going to store all screen components. Inside it, also create `LoginScreen.js`. The Login screen is going to have four main UI elements: - two text input fields for user's email and password - one login button and one button to navigate to sign up screen (_in case the end-user is not registered to use the app_) Start by importing the following statements. ```js import React, { useState } from 'react'; import { View, StyleSheet } from 'react-native'; import { Title } from 'react-native-paper'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; ``` Inside the `LoginScreen` functional component, define two state variables: - `email` - `password` Both of these variables are going to be used with the `FormInput` component to obtain the value of the user credentials. By default, they are going to have an empty string as its value. ```js export default function Login() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); return ( Welcome to Chat app setEmail(userEmail)} /> setPassword(userPassword)} /> ); } ``` Do note that each of the `FormInput` elements has different props being passed. For example, The email component has `autoCapitalize` set to `none`. The password component has `secureTextEntry` set to boolean true. Including others, this is where `...rest` becomes helpful (_as you have seen in the previous section_). The `onChangeText` prop accepts a callback that is invoked whenever the text of the input field changes. Lastly, here are the styles. ```js const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1, justifyContent: 'center', alignItems: 'center' }, titleText: { fontSize: 24, marginBottom: 10 }, loginButtonLabel: { fontSize: 22 }, navButtonText: { fontSize: 16 } }); ``` Do notice that, you are using a JavaScript object to define styles for each component so far. `StyleSheet` in React Native provides an API to create styles inside the component file. It takes a JavaScript object as it does above, and returns a new `Stylesheet` object from it. There are no _classes_ or _ids_ in React Native like in web development. To create a new style object you use `StyleSheet.create()` method. The way you have defined styles by creating an object is the preferred way. Not only it helps you organize styles and keep them separate, but these styles when defined in this manner are also sent through the native render bridge only once (_unlike inline styles_). ## Create a signup screen If the user is not registered to use the app but wants to make a new account to get authorized, this where the signup screen becomes useful. Create a new file called `SignupScreen.js` inside `src/screens/` directory. It is going to be similar to the login screen that you created in the previous section in many ways. I am going to leave it to you to find similarities and differences between the two screens. Take a look at the code snippet for the signup screen below. ```js import React, { useState } from 'react'; import { View, StyleSheet } from 'react-native'; import { Title, IconButton } from 'react-native-paper'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; export default function SignupScreen({ navigation }) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); return ( Register to chat setEmail(userEmail)} /> setPassword(userPassword)} /> navigation.navigate('Login')} /> ); } const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1, justifyContent: 'center', alignItems: 'center' }, titleText: { fontSize: 24, marginBottom: 10 }, loginButtonLabel: { fontSize: 22 }, navButtonText: { fontSize: 18 }, navButton: { marginTop: 10 } }); ``` The major difference in the above component snippet is that you are going to use an `IconButton` to navigate from the signup screen to log in screen. This is provided by `react-native-paper` and is actually a button that displays an icon without any label. ## Create an auth stack navigator There are going to be two stack navigators in the current app. The first navigator is going to be called `AuthStack`. It is going to contain only those screens which allow the user to add their credentials or create credentials. Thus, login screen and sign up screen as routes, where the login screen is going to the initial route. You will learn more about the second stack later. Create a new directory `src/navigation/`. This directory is going to contain all the routes and other necessary components to build the navigation in the app. Inside this directory, create a new file called `AuthStack.js`. This file is going to have a stack navigator. Start by importing the following statements including both screen components. ```js import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import SignupScreen from '../screens/SignupScreen'; import LoginScreen from '../screens/LoginScreen'; ``` A **Stack Navigator** provides the React Native app to transit between different screens similar to how the navigation in a web browser works. It pushes or pops a screen when in the navigational state. Next, create an instance of a stack navigator as below. ```js const Stack = createStackNavigator(); ``` Navigators are defined declaratively using version 5 of `react-navigation`. It follows a more component based approach, similar to that of `react-router` in web development using Reactjs (if you are familiar with it). The `createStackNavigator` is a function used to implement a stack navigation pattern. This function returns two React components: `Screen` and `Navigator`, that help us configure each component screen as shown below. ```js export default function AuthStack() { return ( ); } ``` The `Stack.Navigator` takes those prop values that are common to each screen route. For example, generally, the stack navigator adds a header to each screen inside it. For the current stack, you are not going to require a header on each screen. Thus, setting `headerMode` to the value of `none` fulfills it. The `headerMode` prop specifies how the header should be rendered for each screen in the stack. Setting it to `none`, specifies that it should not be rendered at all. You can find the other values for this mode [here](https://reactnavigation.org/docs/stack-navigator/#headermode). The `initialRouteName` is the name of the route to render on the first load of the navigator. You can learn more Stack Navigator and its common properties in the post [here](https://heartbeat.fritz.ai/getting-started-with-stack-navigator-using-react-navigation-5-in-react-native-and-expo-apps-4c516becaee1). To make the navigation between Login to sign up screen work, you have to add the `navigation` prop to each component. Go to the `LoginScreen.js` file and pass the `navigation` prop reference as a parameter. ```js export default function LoginScreen({ navigation }) { // ... } ``` This prop reference provides a set of functions ready to dispatch as actions for each screen component. Do note that, you can only pass it those screen components that are routes for one of the navigators. For example, in the login screen component, to navigate to sign up screen, add the `onPress` prop to the last `FormButton`. The `navigation.navigate` accepts the value of the screen to navigate to, from the current screen. ```js navigation.navigate('Signup')} /> ``` Similarly, open `SignupScreen.js` screen file, and pass the prop reference for `navigation`. ```js export default function SignupScreen({ navigation }) { // ... } ``` Next, add the `onPress` prop to the `IconButton`. ```js navigation.goBack()} /> ``` The `goBack()` action closes the active screen (Signup screen) and moves back in the stack (Login screen). For more information on the `navigation` prop, check out the official reference [here](https://reactnavigation.org/docs/navigation-prop/). ## Add a navigation container Both of our screen components are now configured for the navigation to work. In this section, let us add the missing piece called `NavigationContainer` to make sure the current navigation in the auth stack works. Create a new file called `Routes.js` inside `src/navigation/` directory. This file is going to contain all the stacks that the app is going to have, but for now, the auth stack. ```js import React from 'react'; import { NavigationContainer } from '@react-navigation/native'; import AuthStack from './AuthStack'; export default function Routes() { return ( ); } ``` The `NavigationContainer` is a component that manages the navigation tree. It also allows the screen components to refer to the `navigation` prop reference. This is done by wrapping all the navigator’s structure. ## Wrapping with the paper provider Create a file called `index.js` inside `src/navigation/` directory. To make UI components from `react-native-paper` to work, you have to wrap all the routes inside `PaperProvider` as shown below. ```js import React from 'react'; import { Provider as PaperProvider } from 'react-native-paper'; import Routes from './Routes'; /** * Wrap all providers here */ export default function Providers() { return ( ); } ``` The `PaperProvider` component provides the theme to all the components in the framework. It also acts as a portal to components that need to be rendered at the top level. This is a mandatory step. The reason to create a separate `Providers` component and wrap `Routes` and not wrap the `App` component (as mentioned in official docs [here](https://callstack.github.io/react-native-paper/getting-started.html#usage)) is that there going to be some custom providers later in this app. So to manage all the providers, it is better if you create a separate file. ## Conclusion The form of screen components is now complete. To make sure they are working as desired, open up a terminal window and build the app for a specific mobile platform. ```shell # for ios npx react-native run-ios # for android npx react-native run-android ``` Then, go to the simulator and you are going to get the following result. --- ## What’s Next? In part one of this tutorial series, you’ve successfully built a navigation flow using the react-navigation library, set up a stack navigator, and learned how to use pre-defined UI components from react-native-paper to create reusable custom form components. In the [next part](https://amanhimself.dev/blog/chat-app-with-react-native-part-2) of this series, we’ll learn how to install the Firebase SDK, how to generate and add Firebase credentials and API keys for iOS apps, implement an email sign-in provider with Firebase, and thus, use the navigation flow with a real-time backend service. You can find the complete source code for this project at [this Github repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp). --- 👉 Here is a list of resources used in this tutorial. - Learn more about [`navigation prop reference`](https://reactnavigation.org/docs/navigation-prop/) - [`Dimensions`](https://reactnative.dev/docs/dimensions) API in React Native - Getting started with stack navigator using `react-navigation` v5 [here](https://heartbeat.fritz.ai/getting-started-with-stack-navigator-using-react-navigation-5-in-react-native-and-expo-apps-4c516becaee1) --- 💙 To learn more about React Native, check out these resources: - [Official documentation](http://reactnative.dev/) Originally published at [Heartbeat.Fritz.Ai](https://heartbeat.fritz.ai/chat-app-with-react-native-part-1-build-reusable-ui-form-elements-using-react-native-paper-75d82e2ca94f) --- ## Chat app with React Native (Part 2) - Firebase Email Authentication with react-native-firebase Slug: chat-app-with-react-native-part-2 ![cover](https://i.imgur.com/ROYjoYo.jpg) In [the first part of this tutorial series](https://amanhimself.dev/blog/chat-app-with-react-native-part-1) to build a chat-based app in React Native, we learned how to create reusable form elements using the react-native-paper UI library. Along with that, we learned how to install the navigation library react-navigation and configure a basic authentication stack navigator using two routes. In this tutorial, let us start using a backend service to add real-time features to the Chat app. For backend services, I am going to use Firebase. You are going to learn how to install and configure Firebase SDK in a react native app with the help of [`react-native-firebase`](https://rnfirebase.io/) module as well as set up and configure Email authentication. In order to follow this tutorial and future posts, you are required to use a Firebase project. ## Create a new Firebase project from console To access the Firebase credentials for each mobile OS platform and configure them to use Firebase SDK, create a new Firebase project or use one if you have access already from [Firebase console](http://console.firebase.google.com/), you can skip this step. Create a new project as shown below. Complete the details of your Firebase project: Click the button **Create project** and you are going to be redirected to the dashboard screen. That's it. You have successfully created a new Firebase project. Now make sure that the **Email** **Sign-in method** is enabled. From the Firebase console and navigate to **Authentication** section from the side menu. Go to the second tab **Sign-in method** and make sure to enable the **Email** sign-in provider. ## Add Firebase SDK to React Native app If you have used `react-native-firebase` version 5 or below, you must have noticed that it was a monorepo that used to manage all Firebase dependencies from one module. Version 6 of this library wants you to only install those dependencies based on Firebase features that you want to use. For example, in the current app, to support the email authentication feature you are going to install the auth and core app package. From the terminal window execute the following command. ```shell yarn add @react-native-firebase/app @react-native-firebase/auth ``` ## Add Firebase credentials to your iOS app Firebase provides a file called `GoogleService-Info.plist` that contains all the API keys as well as other credentials for iOS devices to authenticate the correct Firebase project. To get these credentials, go to back to the [Firebase console](http://console.firebase.google.com/) in a browser window. From the dashboard screen of your Firebase project, open **Project settings** from the side menu. Go to **Your apps** section and click on the icon iOS to select the platform. Enter the application details and click on **Register app**. Then download the `GoogleService-Info.plist` file as shown below. Open Xcode, then open the file `/ios/ChatApp.xcodeproj` file. Right-click on the project name and **Add Files** option, then select the file to add to this project. Then open `ios/ChatApp/AppDelegate.m` and add the following header. ```c #import ``` In the same file, within the `didFinishLaunchingWithOptions` method, add the following configure method. ```c - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } ``` Lastly, go back to the terminal window to install pods. ```js cd ios/ && pod install # after pods are installed cd .. ``` Make sure you build the iOS app. ```js npx react-native run-ios ``` That's it. The configuration to set up a Firebase SDK and credentials in a React Native app is complete. ## Create a home screen In the previous post, you have successfully configured an Auth stack that displays those screens when the end-user is not authorized or logged in inside the app. There are a set of screens that are only going to be accessible to the user when they are logged in. Let us call the group of screens that are visible after login, home stack. One such screen is going to be a home screen where all the chat rooms are going to be listed. In this section, let us start by creating a basic home screen such that you can complete navigational flow between the home stack and the auth stack. Create a new screen component called `HomeScreen.js` inside `src/screens/` directory with the following code snippet. ```js import React from 'react'; import { View, StyleSheet } from 'react-native'; import { Title } from 'react-native-paper'; export default function HomeScreen() { return ( Home Screen All chat rooms will be listed here ); } const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1, justifyContent: 'center', alignItems: 'center' } }); ``` ## Create home stack navigator Create a new stack navigator file called `HomeStack.js` inside `src/navigation.js` that is going to have those routes which are only available after logging in. You can think of these routes as protected routes. Open this file and add the following code snippet. Nothing new is going in terms of creating a stack navigator as shown below. ```js import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import HomeScreen from '../screens/HomeScreen'; const Stack = createStackNavigator(); export default function HomeStack() { return ( ); } ``` ## Create an auth provider In this section, you are going to create an authentication provider to check whether the user is logged in or not and access them if they are logged in. Create a new file called `AuthProvider.js` inside `src/navigation/`. Start by importing the following statements. ```js import React, { createContext, useState } from 'react'; import auth from '@react-native-firebase/auth'; ``` Then create an `AuthContext` and make sure to export it since you are going to use it on several different screens. ```js export const AuthContext = createContext({}); ``` In Reactjs, the [Context API](https://reactjs.org/docs/context.html#reactcreatecontext) is designed to share data that is considered global for a tree of React components. When you are creating a context (like above), there is a requirement to pass a default value. This value is used when a component does not have a matching Provider. The Provider allows the React components to subscribe to the context changes. To create an auth provider, export a function called `AuthProvider`. This provider is going to allow the screen components to access the current user in the application. Define a state variable called `user`. ```js export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); return ( { try { await auth().signInWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, register: async (email, password) => { try { await auth().createUserWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, logout: async () => { try { await auth().signOut(); } catch (e) { console.error(e); } } }} > {children} ); }; ``` In the `value` prop above, also define some functions. These functions are now available to be used anywhere in the screens component tree using React Context. Each of the functions is consuming Firebase methods to interact with real-time Firebase backend service. Both the login and register functions require the user's `email` and `password` to verify/save credentials. The logout method invokes a simple `signOut()` method. All these Firebase methods are available from the `@react-native-firebase/auth` package. Do note that, all these functions are asynchronous actions and thus, using `async await` syntax helps. ## Wrapping routes with auth provider Now, that the provider is created, but how to use for a set of components in the current app tree? Well, you have to wrap this provider around the `Routes` such as to use the helper functions as well as the value of current `user` (as described above) in the screen components. Open `navigation/index.js` file and modify it as follows. ```js import React from 'react'; import { Provider as PaperProvider } from 'react-native-paper'; import { AuthProvider } from './AuthProvider'; import Routes from './Routes'; /** * Wrap all providers here */ export default function Providers() { return ( ); } ``` Remember, from the previous post, we added that comment that to wrap all components using all providers in this file? Well, that's what this file is for. ## Check if the user is logged in or not To check if the user is logged or not, let us modify the `navigation/Routes.js` file. Using the value of the `user` from the auth provider, you are going to switch between the stack navigators. To start, make sure you imported the following statements. ```js import React, { useContext, useState, useEffect } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import auth from '@react-native-firebase/auth'; import AuthStack from './AuthStack'; import HomeStack from './HomeStack'; import { AuthContext } from './AuthProvider'; import Loading from '../components/Loading'; ``` From the above snippet, ignore the `Loading` component for now. You are going to create it at the end of this section. Now, inside the `Routes` function, you are two define two state variables `initializing` and `loading` to check whether the user's state is logged in or not. Also, from the context value, fetch `user` and `setUser`. Then, define a function called `onAuthStateChanged` which is going to handle user state changes. Using `useEffect` hook, you can subscribe to this state change function and make sure you unsubscribe it when the component unmounts. This method allows you to subscribe to real-time events when the user performs an action. The action here can be, logging in, signing out, and so on. ```js export default function Routes() { const { user, setUser } = useContext(AuthContext); const [loading, setLoading] = useState(true); const [initializing, setInitializing] = useState(true); // Handle user state changes function onAuthStateChanged(user) { setUser(user); if (initializing) setInitializing(false); setLoading(false); } useEffect(() => { const subscriber = auth().onAuthStateChanged(onAuthStateChanged); return subscriber; // unsubscribe on unmount }, []); if (loading) { return ; } return ( {user ? : } ); } ``` Lastly, create a new component file called `Loading.js` inside `src/components/` directory. This component is going to be responsible to display a loading spinner. ```js import React from 'react'; import { View, ActivityIndicator, StyleSheet } from 'react-native'; export default function Loading() { return ( ); } const styles = StyleSheet.create({ loadingContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' } }); ``` ## Completing the app In order for the user to perform auth actions in the app, you have to use the context in each of the screen components for different actions. Start by opening `LoginScreen.js`. Import `useContext` from react and `AuthContext` from `AuthProvider`. ```js import React, { useState, useContext } from 'react'; // rest of the import statements remain same import { AuthContext } from '../navigation/AuthProvider'; export default function LoginScreen({ navigation }) { const { login } = useContext(AuthContext); // rest remains statements } ``` Inside the `LoginScreen` function, make sure to add an `onPress` prop as shown below. ```js login(email, password)} /> ``` Similarly, you have to modify the `SignupScreen.js` file. ```js import React, { useState, useContext } from 'react'; // rest of the import statements remain same import { AuthContext } from '../navigation/AuthProvider'; export default function SignupScreen({ navigation }) { const { register } = useContext(AuthContext); // rest remains statements } // Add the onPress prop to register(email, password)} />; ``` Lastly, modify the `HomeScreen` to add a sign out button and when the user is in the logged-in state, display their user `uid` (_the unique identifier in Firebase to differentiate and store different users_). ```js import React, { useContext } from 'react'; import { View, StyleSheet } from 'react-native'; import { Title } from 'react-native-paper'; import { AuthContext } from '../navigation/AuthProvider'; import FormButton from '../components/FormButton'; export default function HomeScreen() { const { user, logout } = useContext(AuthContext); return ( Home Screen All chat rooms will be listed here {user.uid} logout()} /> ); } const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1, justifyContent: 'center', alignItems: 'center' } }); ``` Go to the simulator, and you are going to get similar results as shown below. Perform these steps. Try creating a new user from the sign-up screen, and you are going to get their `uid` on the home screen. You can verify the `uid` of the current user by going to the dashboard screen from Firebase console. ## Conclusion _Congratulations!_ You've completed this tutorial and successfully added an authentication flow between the two stack navigators. In the next part of this series, we'll explore more features such as creating and storing chat rooms in a collection in Firestore, as well as displaying all chat rooms on the home screen. To create a new chat room, we'll create a new modal screen and make changes to the current home stack accordingly. --- ## What's Next? In the [next post](https://amanhimself.dev/blog/chat-app-with-react-native-part-3) of this series, we are going to explore how to create a modal screen using `react-navigation` stack navigator. This modal screen is going to have separate navigator as well as to be used to create a new chat room. Then, we are going to add Firebase NoSQL database Firestore and add a query to store the name of a chat room in a collection. You can find the complete source code for this project at [this Github repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp). --- 👉 Here is a list of resources used in this tutorial: - Reactjs [Context API](https://reactjs.org/docs/context.html#reactcreatecontext) - [Firebase Authentication reference](https://invertase.io/oss/react-native-firebase/v6/auth/quick-start) from `react-native-firebase` - Getting started with stack navigator using `react-navigation` v5 [here](https://heartbeat.fritz.ai/getting-started-with-stack-navigator-using-react-navigation-5-in-react-native-and-expo-apps-4c516becaee1) Originally published at [Heartbeat.Fritz.Ai](https://heartbeat.fritz.ai/chat-app-with-react-native-part-2-firebase-user-authentication-with-react-native-firebase-533352870497). --- ## Chat app with React Native (Part 3) - Create Firestore collections to store chat rooms Slug: chat-app-with-react-native-part-3 ![cover](https://i.imgur.com/ROYjoYo.jpg) In [part 2](https://amanhimself.dev/blog/chat-app-with-react-native-part-2) of this series, we made progress with the chat app by adding email authentication using the real-time auth service from Firebase. This ensures that we have a system in place to authenticate users. In part 3, let's extend our progress by creating and storing chat rooms in real-time using Firestore data storage, provided by the Firebase. We'll continue to explore different tips and best practices for using `react-navigation`. For example, we'll create a modal screen and expand the home stack created in the previous post. ## How to share common header options styles using screenOptions Let us start with a simple yet a very common technique to modify header bar options across various screens in a stack navigator. This technique is a common practice that you will find using yourself with `react-navigation`. Start by modifying the header in the home stack such that any route that is wrapped by `HomeStack` navigator is going to have a similar background color, header tint color, and font size. This is a common practice to configure the header bar and share style properties among different routes in the same stack navigator. Open `src/navigation/HomeStack.js` file and add a `screenOptions` prop to `Stack.Navigator`. ```js export default function HomeStack() { return ( ); } ``` Go back to the simulator and you are going to get the following result. ## Add a separate stack navigator for modal screen In this section, you are going to create a modal screen that will allow the user in the app to create a new chat room. Later in this tutorial, the name of the chat room entered from this screen is going to be stored in the Firestore collection. A _modal screen_ displays the content that temporarily blocks interactions with the main view. It is like a popup and usually has a different transition in terms of opening and closing of the screen. This mode of the screen is generally used to display one specific piece of information. Here's a flowchart to help visualize the navigation flow we're trying to achieve by the end of this section. Start by creating a new screen file called `AddRoomScreen.js` inside `src/screens` directory with the following content. ```js import React from 'react'; import { View, Text } from 'react-native'; import FormButton from '../components/FormButton'; export default function AddRoomScreen({ navigation }) { return ( Create a new chat room navigation.goBack()} /> ); } ``` Right now, focus adding this modal screen to the Home stack navigator rather than its contents. Also, add a temporary button to open the modal screen in the `HomeScreen.js` file. ```js import React, { useContext } from 'react'; import { View, StyleSheet } from 'react-native'; import { Title } from 'react-native-paper'; import { AuthContext } from '../navigation/AuthProvider'; import FormButton from '../components/FormButton'; export default function HomeScreen({ navigation }) { const { user, logout } = useContext(AuthContext); return ( Home Screen All chat rooms will be listed here {user.uid} logout()} /> navigation.navigate('AddRoom')} /> ); } ``` Now open `src/navigation/HomeStack.js` file. In order to keep the modal as a separate route from other home stack routes (such as `HomeScreen`), let us create two new stack navigators in this file. Start by importing the modal screen with the rest of the routes and create two new stack navigator instances. You can give a custom name to each instance. ```js // ... rest of the import statements import AddRoomScreen from '../screens/AddRoomScreen'; // create two new instances const ChatAppStack = createStackNavigator(); const ModalStack = createStackNavigator(); ``` From the snippet, the `ChatAppStack` is going to contain those screens routes that are do not require the use of a modal screen and focus only on the chat app features. ```js function ChatApp() { return ( ); } ``` The Modal stack is going to wrap both the `ChatAppStack` and the modal screen as routes. Modify the exported `HomeStack` as below. Make sure to set the mode of `ModalStack.Navigator` to `modal` and `headerMode` to `none`. ```js export default function HomeStack() { return ( ); } ``` Go to the simulator. You are going to find the `Add room` button on the home screen as shown below. Click on the button and notice the transition when the modal screen pops up. ## How to add an icon in the header bar The modal stack is working as per the requirement. But the way the user would navigate from the home screen to modal is not by clicking a button in the center of the home screen. This action is going to be done by clicking an icon button from the header. Luckily, the `react-navigation` library provides props for us to implement this action without any hassle. Import `IconButton` from `react-native-paper` UI library inside the file `src/navigation/HomeStack.js`. ```js // rest of the imports import { IconButton } from 'react-native-paper'; ``` Then add an `options` prop with a function such that you are able to pass `navigation` prop reference. Add the following code to the `HomeScreen` route. ```js ({ headerRight: () => ( navigation.navigate('AddRoom')} /> ) })} /> ``` Also, remove `FormButton` in `HomeScreen.js` you create in the previous section. Here is how the home screen in the simulator looks like after this step. ## Complete the modal screen Right now the modal screen just displays a line of text and a close button but the real functionality this screen has to provide is to allow the user to enter the name of the chat room using an input field. Then, using a form button, add the chat room name in a Firestore collection. Open `AddRoomScreen.js` and start by modifying the import statements. ```js import React, { useState } from 'react'; import { View, StyleSheet } from 'react-native'; import { IconButton, Title } from 'react-native-paper'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; ``` Then, to add a chat room, define a state variable called `roomName` inside a functional component `AddRoomScreen`. To modify the JSX returned from this component. Make sure to add a close button at the right corner of the screen and using custom components you can add the input field as well as the submit button. ```js export default function AddRoomScreen({ navigation }) { const [roomName, setRoomName] = useState(''); // ... Firestore query will come here later return ( navigation.goBack()} /> Create a new chat room setRoomName(text)} clearButtonMode="while-editing" /> handleButtonPress()} disabled={roomName.length === 0} /> ); } ``` Do not worry about the `handleButtonPress` method on `onPress` prop for `FormButton`. This is going to execute the Firestore query and that is what you are going to do from the next section. The corresponding styles of the above component are defined as below. ```js const styles = StyleSheet.create({ rootContainer: { flex: 1 }, closeButtonContainer: { position: 'absolute', top: 30, right: 0, zIndex: 1 }, innerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' }, title: { fontSize: 24, marginBottom: 10 }, buttonLabel: { fontSize: 22 } }); ``` If you go to the modal screen, you are going to get the following result. Here is the complete flow of the `HomeStack` navigator so far. The **Create** button will remain disabled unless the user starts typing. ## Add Firestore to the Chat app To store messages as well as user information, let us use the Firestore data storage service from Firebase. Firestore has similarities to a NoSQL database (if you are familiar with NoSQL types). To use the Firestore database, all you have to do is install the `@react-native-firebase/firestore` package and run the command to build the app again. Open up a terminal window and execute the following command. ```shell yarn add @react-native-firebase/firestore # do not forget to install pods for ios cd ios / && pod install # after pods have been installed cd .. ``` Do note that, the Firestore package from `react-native-firebase` depends on two other packages: - `@react-native-firebase/app` - `@react-native-firebase/auth` This means that these two packages are required to install to use Firestore. For the current app, you have already installed these packages so you do not have to install them again. The last step in this section is to rebuild the app for each OS. ```shell # for iOS npx react-native run-ios # for Android npx react-native run-android ``` That's it to install Firestore. ## Create a collection in firestore to store chat rooms Each chat room is going to contain `x` number of messages between different users. To store a chat room in the Firestore, let's create a collection called `THREADS`. Start by importing `firestore` in the `AddRoomScreen.js` file. ```js // after other import statements import firestore from '@react-native-firebase/firestore'; ``` Inside the functional component `AddHomeScreen` add a handler method called `handleButtonPress`. This method is going to have the business logic to store the name of the chat room under the collection `THREADS`. The unique id of each chat room is going to be created by the Firestore itself. ```js function handleButtonPress() { if (roomName.length > 0) { firestore() .collection('THREADS') .add({ name: roomName } }) .then(() => { navigation.navigate('Home'); }); } } ``` Go back to the simulator and try to create a new chat room. After that, go to the Firebase database console and verify if the `THREADS` collection has a room called `Room 1` or not. ## Display a list of chat rooms on the home screen To display chat rooms from Firestore you are going to make use of `FlatList` form React Native. Start by adding the following the import statements inside the `src/screens/HomeScreen.js` file. ```js import React, { useState, useEffect } from 'react'; import { View, StyleSheet, FlatList } from 'react-native'; import { List, Divider } from 'react-native-paper'; import firestore from '@react-native-firebase/firestore'; import Loading from '../components/Loading'; ``` Inside the functional component `HomeScreen`, define two state variables: - `threads` that is going to be used as the source of data for the FlatList component after the data has been fetched from the Firestore. - `loading` variable is going to keep track of whether the data is being fetched or not. ```js export default function HomeScreen() { const [threads, setThreads] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const unsubscribe = firestore() .collection('THREADS') .onSnapshot(querySnapshot => { const threads = querySnapshot.docs.map(documentSnapshot => { return { _id: documentSnapshot.id, // give defaults name: '', ...documentSnapshot.data() }; }); setThreads(threads); if (loading) { setLoading(false); } }); /** * unsubscribe listener */ return () => unsubscribe(); }, []); if (loading) { return ; } // ...rest of the component } ``` Using the hook `useEffect` in the above snippet you can query the Firestore to fetch the name of chat rooms from the collection `THREADS`. When the component loads, to fetch the existing chat rooms or in other words, to read the data from the Firestore, start by declaring a `unsubscribe` listener to the query. This listener is going to subscribe to any updates. These updates can be new or existing chat rooms. Declaring a listener here is important because when the screen unmounts, it is important to unsubscribe from this listener. Using the `querySnapShot`, you are going fetch every document or the chat thread is going to be the part of the the state variable threads. At this point, data is returned from the query, as well as a default object that contains the `_id`(required as unique if for each item in the `FlatList` component), and the name of the chat room. Here is the complete JSX rendered by this component. ```js item._id} ItemSeparatorComponent={() => } renderItem={({ item }) => ( )} /> ``` The [`Divider` component](https://callstack.github.io/react-native-paper/divider.html) is a lightweight separator provided by UI library `react-native-paper`. Here are the styles associated with the above JSX. ```js const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1 }, listTitle: { fontSize: 22 }, listDescription: { fontSize: 16 } }); ``` Go back to the simulator device and you are going to get the following result. ## Conclusion The main objective of this tutorial is to create and store chat room names in a Firestore cloud database collection as well as integrate the configure the Firestore in our current app. This objective has been completed among other tips and techniques to create a modal screen and share header bar modifications among different route screens. ## What's Next? In the [next part](https://amanhimself.dev/blog/chat-app-with-react-native-part-4) of this series, we are going to explore how to integrate and use [`react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat) which is one of the most important, open source, and actively maintained library to use when building a chat app using React Native. The "out of the box" features it provides in terms of mere props are so helpful and saves a ton of development time. You can find the complete source code for this project at [this Github repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp). 👉 Here is a list of resources used in this tutorial: - Reactjs [Context API](https://reactjs.org/docs/context.html#reactcreatecontext) - [Firebase Authentication reference](https://invertase.io/oss/react-native-firebase/v6/auth/quick-start) from `react-native-firebase` - Getting started with stack navigator using `react-navigation` v5 [here](https://heartbeat.fritz.ai/getting-started-with-stack-navigator-using-react-navigation-5-in-react-native-and-expo-apps-4c516becaee1) --- ## Chat app with React Native (Part 4) - A guide to create Chat UI Screens with react-native-gifted-chat Slug: chat-app-with-react-native-part-4 ![cover](https://i.imgur.com/ROYjoYo.jpg) In [part 3](https://amanhimself.dev/blog/chat-app-with-react-native-part-3), we completed the task of integrating the Firestore to the current React Native app. The database now stores a chat room name. A new chat room can be created using a modal stack, only if the user is authenticated. In part 4, let us proceed with further and a new screen that allows the user to send and receive messages as well as display those messages inside a chat room. To fulfill this purpose, let us use an open-source library called [`react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat). You are going to learn how to integrate it within the current React Native app and learn how to use its "out of the box" features as props to save saves a ton of development time. To begin, make sure to install this module by executing the following command from a terminal window. ```shell yarn add react-native-gifted-chat ``` ## Add a new screen to display messages Start by adding a new screen file called `RoomScreen.js` inside `src/screens/` directory. This file is going to be used to display messages inside each chat room. Then, let us add a mock chat UI screen elements to this screen. This can be done in the following steps: - import `GiftedChat` from `react-native-gifted-chat`. This component is going to be essential in adding UI and chat functionalitie s - Create a functional component `RoomScreen`, inside it, define a state variable called `messages`. This variable is going to have an empty array as its default value. - Add some mock message data objects. Display two types of messages in each object. The first object is going to be a system message which showcases information like "The following chat room was created at X time...". The second object is going to hold a `text` message that is going to have a `user` object associated and contains user information, such as user name. Both of these messages are going to have a unique `_id`. - Create a helper method called `handleSend` that is going to be used when sending a message in a particular chat room. - Lastly, return the following code snippet. The `newMessage` is concatenated with previous or the initial messages using `GiftedChat.append()` method. ```js import React, { useState } from 'react'; import { GiftedChat } from 'react-native-gifted-chat'; export default function RoomScreen() { const [messages, setMessages] = useState([ /** * Mock message data */ // example of system message { _id: 0, text: 'New room created.', createdAt: new Date().getTime(), system: true }, // example of chat message { _id: 1, text: 'Henlo!', createdAt: new Date().getTime(), user: { _id: 2, name: 'Test User' } } ]); // helper method that is sends a message function handleSend(newMessage = []) { setMessages(GiftedChat.append(messages, newMessage)); } return ( handleSend(newMessage)} user={{ _id: 1 }} /> ); } ``` ## Change RoomScreen to stack Navigator Each message thread is only going to be displayed when the user enters the chat room. Open `src/navigation/HomeStack.js` and add the `RoomScreen` component as the second screen to the `ChatApp` stack as shown below. ```js import React from 'react'; import { createStackNavigator } from '@react-navigation/stack'; import { IconButton } from 'react-native-paper'; import HomeScreen from '../screens/HomeScreen'; import AddRoomScreen from '../screens/AddRoomScreen'; // Add this import RoomScreen from '../screens/RoomScreen'; const ChatAppStack = createStackNavigator(); const ModalStack = createStackNavigator(); function ChatApp() { return ( ({ headerRight: () => ( navigation.navigate('AddRoom')} /> ) })} /> {/* Add this */} ); } // rest of the code remains same ``` Then, open `src/screebs/HomeScreen.js` file, and make sure to pass the `navigation` reference as prop to the function component: `export default function HomeScreen({ navigation }) {...}`. Each chat room is displayed as an item in the FlatList. You will have to make it pressable to allow the user to enter the chat room and display the `RoomScreen` component. Each list item can be wrapped in the `TouchableOpacity` component such that using `navigation` prop reference as the value of `onPress`, the user is allowed to navigate to the next screen. Here is the complete code snippet after the modifications. ```js import React, { useState, useEffect } from 'react'; import { View, StyleSheet, FlatList, TouchableOpacity } from 'react-native'; import { List, Divider } from 'react-native-paper'; import firestore from '@react-native-firebase/firestore'; import Loading from '../components/Loading'; export default function HomeScreen({ navigation }) { const [threads, setThreads] = useState([]); const [loading, setLoading] = useState(true); /** * Fetch threads from Firestore */ useEffect(() => { const unsubscribe = firestore() .collection('THREADS') // .orderBy('latestMessage.createdAt', 'desc') .onSnapshot(querySnapshot => { const threads = querySnapshot.docs.map(documentSnapshot => { return { _id: documentSnapshot.id, // give defaults name: '', ...documentSnapshot.data() }; }); setThreads(threads); if (loading) { setLoading(false); } }); /** * unsubscribe listener */ return () => unsubscribe(); }, []); if (loading) { return ; } return ( item._id} ItemSeparatorComponent={() => } renderItem={({ item }) => ( navigation.navigate('Room', { thread: item })} > )} /> ); } const styles = StyleSheet.create({ container: { backgroundColor: '#f5f5f5', flex: 1 }, listTitle: { fontSize: 22 }, listDescription: { fontSize: 16 } }); ``` Go to the simulator window and you are going to get the following result. Great! The chat UI for each room is now accessible. Try to send a message, of course, it won't get saved since there is no database connected yet. Once the user exits the room and comes back later, only the mock message is displayed. Do notice that the system message `New room created` is displayed as well. ## Display title of each room When you enter the chat room, did you notice that the name of the room is not being displayed correctly? It just says `Room` whereas the complete name of the first room should be `Room 1`. Let us fix this in the current section. Open `HomeStack.js` file and modify the route for the `RoomScreen` component by adding `options` to it. The value of the title for each chat room is going to be the name of that chat room. This can be obtained using `route` props as shown below. ```js ({ title: route.params.thread.name })} /> ``` When using the `react-navigation` library for routing, each screen component is provided with the `route` prop automatically. This prop contains various information regarding the current route such as a place in navigation hierarchy the route component lives. `route.params` allows access to a set of params defined when navigating. These sets of params have the name of the same chat room as stored in Firestore because in the previous section you did pass the object `thread`. ```js navigation.navigate('Room', { thread: item })}> ``` Here is the output you are going to get on the device. ## Modifying the Chat screen UI: Changing the chat bubble Gifted chat module gives an advantage for creating a Chat UI in a React Native app over building the UI from scratch. This advantage comes in the form of [props available](https://github.com/FaridSafi/react-native-gifted-chat#props) in this package. Right now the chat bubble appears as shown below. Let us change the background color of this bubble to reflect the same color as in the header bar (which is used at many instances in the app). This is going to be done in the following steps: - Start by importing the `Bubble` from the gifted chat module. - Create a helper method `renderBubble` inside function component `RoomScreen` - Return the `` component from the helper function with new styles. The style properties are defined in the Gifted chat module so make sure to use the same property names. - Lastly, on the `GiftedChat` component, enter the prop `renderBuble`. ```js // Step 1: modify the import statement import { GiftedChat, Bubble } from 'react-native-gifted-chat'; export default function RoomScreen() { // ... // Step 2: add a helper method function renderBubble(props) { return ( // Step 3: return the component ); } return ( handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} /> ); } ``` With that done, here is the output you are going to get. ## Adding other modifications to Chat UI You can modify the placeholder text using the prop `placeholder` as shown below. ```js handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." /> ``` Previously the placeholder text says: After adding the `placeholder` prop, it looks like: You can add the prop `showUserAvatar` to always display the user avatar of the current user. ```js handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar /> ``` Right now, the send button only appears when the user is typing a message. Add the prop `alwaysShowSend` to always show the send button to the current user. ```js handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar alwaysShowSend /> ``` ## Add a custom send button You can also modify this send button to show a custom text or icon. Let us do that to show a custom send icon. This is going to be done in the following steps. - Import the `Send` component form Gifted chat API. - Import `IconButton` from `react-native-paper`. - INside the functional component `RoomScreen`, add a helper method `renderSend` that is going to return the `IconButton` component. - Add the prop `renderSend` to ``. - Add corresponding styles if any. ```js // Step 1: import Send import { GiftedChat, Bubble, Send } from 'react-native-gifted-chat'; // Step 2: import IconButton import { IconButton } from 'react-native-paper'; import { View, StyleSheet } from 'react-native'; export default function RoomScreen() { // ... // Step 3: add a helper method function renderSend(props) { return ( ); } return ( handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar alwaysShowSend // Step 4: add the prop renderSend={renderSend} /> ); } // Step 5: add corresponding styles const styles = StyleSheet.create({ sendingContainer: { justifyContent: 'center', alignItems: 'center' } }); ``` Here is the output you are going to get after this step. ## Add a scroll to the bottom button Right now, in the Chat UI, there is no way for the current user to scroll to the latest message. They have to manually scroll down to see the latest message in the thread. Here is a demo of the problem. This can be solved by adding prop `scrollToBottom`. ```js handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar alwaysShowSend renderSend={renderSend} scrollToBottom /> ``` Take a look at the down caret sign at the right side of the app shown below. This is not pleasing at all with the current background of the screen. Let us modify this button with a custom background. This can be done in three simple steps. - Add a helper method inside `RoomScreen` functional component and call this helper method `scrollToBottomComponent()`. Use `IconButton` component from `react-native-paper` to customize this button. - Add the prop `scrollToBottomComponent` to ``. - Add corresponding styles to the `styles` object. ```js export default function RoomScreen() { // ... // Step 1: add helper method function scrollToBottomComponent() { return ( ); } return ( handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar alwaysShowSend renderSend={renderSend} // Step 2: add the prop scrollToBottomComponent={scrollToBottomComponent} /> ); } // Step 3: add corresponding styles const styles = StyleSheet.create({ // rest remains same bottomComponentContainer: { justifyContent: 'center', alignItems: 'center' } }); ``` Here is the output. ## Add a loading spinner when the room screen initializes Initializing a new screen or in the current case, a chat room may take some time. It is good practice to add a loading indicator to convey the message to the user when they enter the chat room. This can be done by adding a prop called `renderLoading` which returns an `ActivityIndicator` from `react-native` core API. - Import the `ActivityIndicator` from `react-native` core API. - Add helper method `renderLoading()` to functional component `RoomScreen`. - Add the prop `renderLoading` to ``. - Add corresponding styles. ```js // Step 1: import ActivityIndicator import { ActivityIndicator, View, StyleSheet } from 'react-native'; export default function RoomScreen() { // ... // Step 2: add a helper method function renderLoading() { return ( ); } return ( handleSend(newMessage)} user={{ _id: 1, name: 'User Test' }} renderBubble={renderBubble} placeholder="Type your message here..." showUserAvatar alwaysShowSend renderSend={renderSend} scrollToBottomComponent={scrollToBottomComponent} // Step 3: add the prop renderLoading={renderLoading} /> ); } // Step 4: add corresponding styles const styles = StyleSheet.create({ // rest remains same loadingContainer: { flex: 1, alignItems: 'center', justifyContent: 'center' } }); ``` On the current screen you might see a loading indicator when you refresh the app for the first time or when the screen initializes for the first time. ## What's Next? In [part 5 of this series](https://amanhimself.dev/blog/chat-app-with-react-native-part-5), we are going to create messages in real-time using the Firestore database. We will be covering how using react-navigation you can get the current room's id. Then, use it with the current user from the `AuthContext` we created earlier, to add real-time message information such as a text field and a timestamp associated with it. We will then add another real-time feature to display the latest message on the home screen under each room name's description using Firestore queries. You can find the complete source code for this project at [this Github repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp). --- 👉 Here is a list of resources used in this tutorial: - [React Native Gifted Chat module](https://github.com/FaridSafi/react-native-gifted-chat) - [Props available for `react-native-gifted-chat`](https://github.com/FaridSafi/react-native-gifted-chat#props) ## Further Reading - [React - Separation of Concerns by Andrei Calazans](https://www.g2i.co/blog/react-separation-of-concerns) [Originally Published at Heartbeat.Fritz.ai](https://heartbeat.fritz.ai/chat-app-with-react-native-part-4-create-chat-ui-screens-with-react-native-gifted-chat-7ef428a60d30) --- ## Chat app with React Native (Part 5) - Create and Fetch Real-Time Messages with Firestore Slug: chat-app-with-react-native-part-5 ![cover](https://i.imgur.com/ROYjoYo.jpg) In [part 4](https://amanhimself.dev/blog/chat-app-with-react-native-part-4), we built the foundation of creating a chat app by adding UI screens that are focused on sending, receiving and displaying chat messages. We used `react-native-gifted-chat` an amazing open source library and dived deep to use its "out of the box" props to add features to the chat app. In part 5, we are going to connect every chat functionality that we built so far with a real-time database service from Firebase, called Firestore. You are going to learn - store chat messages of each thread/chat room in Firestore collection - how to create sub collections inside a Firestore collection - add a feature to display most recent message for each chat room on home screen - fetch data from a Firestore collection And few other things along the way. Let's get started. ## How to get current user information in the app? Remember, in [part 2](https://amanhimself.dev/blog/chat-app-with-react-native-part-2), when configuring Email authentication between the chat app and the Firebase service, you set the following `AuthProvider` that gives access to the current user as well other methods that are already being used in components `LoginScreen` and `SignupScreen`. Here is the ode for `src/navigation/AuthProvider.js` for your reference. ```js import React, { createContext, useState } from 'react'; import auth from '@react-native-firebase/auth'; /** * This provider is created * to access user in whole app */ export const AuthContext = createContext({}); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); return ( { try { await auth().signInWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, register: async (email, password) => { try { await auth().createUserWithEmailAndPassword(email, password); } catch (e) { console.log(e); } }, logout: async () => { try { await auth().signOut(); } catch (e) { console.error(e); } } }} > {children} ); }; ``` To fetch the logged in user information (aka the current user), start by importing `AuthContext` in the file `RoomScreen.js`. ```js // ... rest of the import statements import React, { useContext, useEffect } from 'react'; import { AuthContext } from '../navigation/AuthProvider'; ``` Next, to verify that the you are getting the current user information, inside the `RoomScreen` component, add the following two lines. ```js export default function RoomScreen({ route }) { const { user } = useContext(AuthContext); const currentUser = user.toJSON(); // ... } ``` You have to convert the user data being fetched in JSON object. To check that the user data is incoming, let us temporarily add a `useEffect` hook after the previous code snippet, as shown below. ```js useEffect(() => { console.log({ user }); }, []); ``` ## How to use Chrome Dev tools with a React Native app? There are two ways to check the output of console statements in a React Native app. First, a console statement triggers, in the terminal window, the will be a `LOG` entry like below with desired result. However, for better complete control over [debugging](https://reactnative.dev/docs/debugging), you can use Chrome dev tools. This can be done by opening the in-app developer menu, either by shaking the device or if you are using an iOS simulator press `command + d`. On Android, you have to press `command + m` on mac (for windows, press `control + m`). A developer menu like below will popup. Select the option `Debug`. In your default Chrome browser, it is going to open like below. Go to **Console** tab. Enter a chat room from the app. If you do not have to created a chat room yet, create one. On the Console tab, you are going to get the following result. That's it. Now, from the above image, you can definitely verify that a user is logged in and their email credentials can be verified. ## How to store messages in Firestore? In this section, you are going to add the business logic as well as the ability to store the chat conversation between multiple users in a chat room. These messages are going to be stored in a sub collection. The main reason to create a sub collection is that when a new chat room is created, storing every data associated to that chat room in its own collection is a good idea. That said, when a new chat room is created, inside the collection `THREADS` a new document with a unique identifier is generated. Inside that, you are going to add another collection called `MESSAGES` that is only going to store chat conversation that happens in that chat room. This will get clear as you proceed in this section. Start by importing the some necessary React Hooks as shown below. Also, import `firestore` to make queries to create new sub-collection, and fetch data. ```js import React, { useState, useContext, useEffect } from 'react'; import firestore from '@react-native-firebase/firestore'; ``` To get the `id` of the current chat room (_this is important_) you have to pass the `route` as a parameter to the `RoomScreen` functional component. Since, from the previous screen, a `thread` object is passed which gives the chat room id (_or thread id_) store in the Firebase collection `THREADS`. Using `route.params` you can get the whole `thread` object. This is possible because of `react-navigation`. ```js export default function RoomScreen({ route }) { // ... rest of the code const { thread } = route.params; } ``` Next, modify the asynchronous helper method `handleSend`. This method is used to send a message as you might have already seen in part 4. Inside this helper method, get the text of each message send by the user. Then, create the sub collection `MESSAGES` by referencing the correct id of the current thread the user is conversing in. Using `add()` you can add anew document with an auto-generated unique id for each message inside the sub collection. Pass on an object with fields like `text` that represents the text of each message, the timestamp it is being send or created at, and the user information (such as user's `uid`, and `email`). ```js async function handleSend(messages) { const text = messages[0].text; firestore() .collection('THREADS') .doc(thread._id) .collection('MESSAGES') .add({ text, createdAt: new Date().getTime(), user: { _id: currentUser.uid, email: currentUser.email } }); } ``` Go back to the simulator, create a new room, and send a message. In Firebase console, you are going to notice that the inside the `THREADS` collection, a sub-collection called `MESSAGES` is created as shown below. Ignore the `latestMessage` field, we will cover that in the next section. The image below displays that the messages are being stored with correct information. ## Display the latest message for each chat room on home screen In this section, you are going to update the `THREADS` collection with a new field called `latestMessage` that you have already seen in the previous section, in Firebase console. The advantage this field is going to give us (which we will complete later) is to show the last or the latest message send in a particular chat room, to be displayed on the home screen where a room's description field already exists. This will save the user time to glance at the last message without opening the room to see if there are any new messages or not. To begin, all you have to do is refer the current thread using its id, then `set` an object that has field `latestMessage` with `text` and `createdAt` timestamp properties. Then pass on the second object that has a property of `merge`. ```js async function handleSend(messages) { // ... await firestore() .collection('THREADS') .doc(thread._id) .set( { latestMessage: { text, createdAt: new Date().getTime() } }, { merge: true } ); } ``` In Firestore, when `set` is used with `merge`, it update fields in a document or create that document if it does not exists. If you use `set` here without `merge`, it will overwrite the whole document. ## How to fetch messages from Firestore to display in chat room? To display messages in a chat room once they send by a user, these messages have to be fetched from the Firestore sub-collection created previous sections, `MESSAGES`. To fetch the data, let us use `useEffect` hook. [The effect hook](https://reactjs.org/docs/hooks-effect.html) lets you add side-effects to functional components. In the previous versions of React and React Native, this could be done by using lifecycle methods such as `componentDidMount()` and other different methods in class components. The `useEffect` hook can perform multiple side-effects such as data fetching and more in different ways. To fetch the messages, first you have to traverse inside the current thread using its id, then the sub-collection `MESSAGES`. When traversing the sub-collection, make sure to order the messages to display them in descending order according to the time they were sent. Then using a `querySnapshot` you can `map` the messages array from the sub collection. A Query Snapshot in Firestore contains zero objects or more objects inside an array representing the results of a query. Create a data object that is going to contain the `id` of the document being fetched, the text of the message and its timestamp, and any other data associated with the message or in the document. The last step is required to identify that if the message is send by the user or is system generated. In part 4 you have seen how a system generated message looks like. This means, if the message is generated when the chat room was created or not. If the message is not system generated, that means it is send by the user. You will have to add the user's email (or any other details can be added such as user's display name)to the `data` object. Add the following snippet. ```js async function handleSend(messages) { // ... useEffect(() => { const messagesListener = firestore() .collection('THREADS') .doc(thread._id) .collection('MESSAGES') .orderBy('createdAt', 'desc') .onSnapshot(querySnapshot => { const messages = querySnapshot.docs.map(doc => { const firebaseData = doc.data(); const data = { _id: doc.id, text: '', createdAt: new Date().getTime(), ...firebaseData }; if (!firebaseData.system) { data.user = { ...firebaseData.user, name: firebaseData.user.email }; } return data; }); setMessages(messages); }); return () => messagesListener(); }, []); } ``` The messages in chat room are going to be displayed as the following. In order to make all this work, make sure to modify the following two props in return statement. ```js ); ``` ## How to set a system message as latest message in a chat room? Right now the `THREADS` collection for each chat room can display the latest message sent by the user but when a thread is created, you might want to display a system, generated message to convey the same message to the user entering the chat room. To do this, open `AddRoomScreen.js` file and modify its its helper method `handleButtonPress` to add the following snippet. First you are going to add the `latestMessage` object with its text field saying that a room is created. Do not forget to add a timestamp field along with the text field. Second step is to add a `docRef` or a document reference to the sub-collection `MESSAGES`. Note that, at this point, when the user creates a new room, this sub-collection will be created for each chat room. A document reference in Firestore is used to write, read or listen to a particular location or a sub-collection inside a Firestore collection. The document or in the current case, the collection `MESSAGES` might not exist but adding this step will create the collection. This first message in a chat room is also going to be the system generated message. ```js function handleButtonPress() { if (roomName.length > 0) { firestore() .collection('THREADS') .add({ name: roomName, latestMessage: { text: `You have joined the room ${roomName}.`, createdAt: new Date().getTime() } }) .then(docRef => { docRef.collection('MESSAGES').add({ text: `You have joined the room ${roomName}.`, createdAt: new Date().getTime(), system: true }); navigation.navigate('Home'); }); } } ``` Now, when you create a new room through the app, here is the complete overview of how it gets reflected in Firestore. And here is the system message displayed in the new chat room. ## Customizing the system message in react-native-gifted-chat Right now the system message generated is not as appealing and conveying inside a chat room. In this short section, let us learn how to customize that in `react-native-gifted-chat`. Start by importing `SystemMessage` component from `react-native-gifted-chat` inside `RoomScreen.js` file. ```js import { GiftedChat, Bubble, Send, // Add this SystemMessage } from 'react-native-gifted-chat'; ``` Create a new helper method called `renderSystemMessage` inside the screen component with the following snippet. In the current scenario, you are going to change the background of the system message display as well as the text styles. For that you need to edit the props `wrapperStyle` and `textStyle` of `SystemMessage` component. Do modify the `StyleSheet` object to add styles as shown below. ```js function renderSystemMessage(props) { return ( ); } // appropriate styles const styles = StyleSheet.create({ // ... rest of the styles remain unchanged systemMessageText: { fontSize: 14, color: '#fff', fontWeight: 'bold' } }); ``` Lastly, add the prop `renderSystemMessage` to `GiftedChat` component. ```js return ( ); ``` Here is the output you are going to get after this step. ## How to display latest message on home screen? For every chat room on home screen there is description field that says a static message `Item description`. In this section let us change that to dynamically display the real-time latest message fetched from the Firestore collection. Open `HomeScreen.js` and `orderBy()` when fetching name of chat rooms in the Effect hook. Then, when returning the documentSnapShot data, there is an object that contain fields like `_id` and `name`. Add another object as a field called `latestMessage` as shown below. ```js useEffect(() => { const unsubscribe = firestore() .collection('THREADS') // add this .orderBy('latestMessage.createdAt', 'desc') .onSnapshot(querySnapshot => { const threads = querySnapshot.docs.map(documentSnapshot => { return { _id: documentSnapshot.id, name: '', // add this latestMessage: { text: '' }, // --- ...documentSnapshot.data() }; }); setThreads(threads); if (loading) { setLoading(false); } }); return () => unsubscribe(); }, []); ``` Next, go to the `List.Item` inside the `FlatList` component and modify the description field as shown below. ```js description={item.latestMessage.text} ``` Go back to the simulator and you are going to see the latest message displayed. Try sending a new message and that is going to be the latest message displayed on the home screen for the chat room. There is a benefit of ordering the chat rooms according to the latest message for each room. Now the home screen is going to display that chat room on top which received it the most recent message according the timestamp (createdAt)that is associated with the message. ## What's Next? In the [next part](https://amanhimself.dev/blog/chat-app-with-react-native-part-6) of the series we are going to fix a small bug related of status bar styles for every screen component in the current app. This is going to be done by creating a custom hook and using `react-navigation`. 😺 **You can find the complete code here at this [GitHub repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp).** ## Further Reading - [React Native’s New Architecture — Glossary of terms by Gabe Greenberg](https://www.g2i.co/blog/react-natives-new-architecture-glossary-of-terms) - [The Effect hook in React](https://reactjs.org/docs/hooks-effect.html) - [Debugging React Native apps](https://reactnative.dev/docs/debugging) [Originally Published at Heartbeat.Fritz.ai](https://heartbeat.fritz.ai/chat-app-with-react-native-part-5-create-and-fetch-real-time-messages-with-firestore-86fb012edaf5) --- ## Chat app with React Native (Part 6) - Create a custom hook to change status bar styles Slug: chat-app-with-react-native-part-6 ![cover](https://i.imgur.com/ROYjoYo.jpg) In [part 5](https://amanhimself.dev/blog/chat-app-with-react-native-part-5), we successfully connected real-time database service Firestore to store chat messages in a collection where each collection would represent a separate chat room. Further, we built sub-collections in each chat room collection to store and identify latest messages from all other messages in a chat room. This part is going to be a bit different. Instead of writing code to communicate with any real-time service, we are going to fix a bug by creating a custom hook. Here is a screen shot of the type of bug I am talking about it. Notice how the status bar remains dark in color on both lighter and darker backgrounds. Do notice that the status bar looks fine when the background is light in colour. This happens when the modal screen to create a new chat room is displayed. But on rest of the screens, when chat rooms are displayed or inside a chat room, the status bar does not matches well with the coloured background of header on both of these screens. React Native has a core component in its API called `StatusBar` that is used to control the app status bar behavior and its styles. However, manually adding `StatusBar` to each screen is not our goal in this tutorial. The navigation library `react-navigation` is going to help us to solve this. We are going to create a custom hook that is going to track the status bar color and change it accordingly whenever a screen changes. That is, on the lighter background, a dark status bar is displayed and on a darker background of the screen, a light status bar is displayed. To begin you need `react-navigation` library to be installed. Since we have already done that in [part 1](https://heartbeat.fritz.ai/chat-app-with-react-native-part-1-build-reusable-ui-form-elements-using-react-native-paper-75d82e2ca94f) of this chat app series. If you just came across this tutorial, please have a look at part 1 and instructions mentioned on how to install and configure `react-navigation` library. Otherwise, you can follow the instructions from `react-navigation` library official docs [here](https://reactnavigation.org/). ## Create a custom Status bar hook The `react-navigation` library provides a hook called `useFocusEffect` that helps to run side-effects when a specific screen is focused. A side effect can be described as fetching data, updating a title, running an event listener and so on. This hooks is similar to `useEffect` hook from React with the difference being between the two is that side-effects in `useFocusEffect` run only when a screen component is focused. Let us begin to develop this custom hook. Create a new file called `useStatusBar.js` inside `src/utils/` directory. Import the following statements. ```js import React, { useCallback } from 'react'; import { StatusBar } from 'react-native'; import { useFocusEffect } from '@react-navigation/native'; ``` Export a custom function called `useStatusBar`. This function is going to be act as a custom hook that is going to provide a simple way to change the color of the status bar when applied. Pass the `style` as the only parameter for now. The value of the style is going to be determined on the screen component this hook is used. ```js export default function useStatusBar(style) { useFocusEffect( useCallback(() => { StatusBar.setBarStyle(style); }, []) ); } ``` It is important to wrap the `StatusBar` with `React.useCallback` hook to avoid triggering the side-effect after every render when the screen is focused. ## Application of the custom hook To apply this hook for the first time, open `screen/HomeScreen.js` file, import the custom hook and apply it as shown below. ```js // rest of the import statements import useStatsBar from '../utils/useStatusBar'; export default function HomeScreen({ navigation }) { useStatsBar('light-content'); // rest of the code remains same } ``` Go back to the simulator and you are going to notice the changes now. It works. If you look closely at the header bar of the `Home` screen, you are going to see that the status bar has the value of light styles applied. However, this style is also applied to all of the other screens, even on those screens such as `AddRoomScreen` where a darker status bar would be preferred. To fix this, you have to explicitly mention the styles of the status bar for each component using the custom hook we created in the previous section. ```js /** * screens/AddRoomScreen.js */ // rest of the import statements import useStatsBar from '../utils/useStatusBar'; export default function AddRoomScreen({ navigation }) { useStatsBar('dark-content'); // rest of the code remains same } /** * screens/RoomScreen.js */ // rest of the import statements import useStatsBar from '../utils/useStatusBar'; export default function RoomScreen({ route }) { useStatsBar('light-content'); // rest of the code remains same } ``` Now, go back to the simulator and you are going to find everything is in order and works as expected. ## Animate the value for smoother transitions For better transitions between different screens in a stack navigator, you can pass the second parameter to the `useStatusBar`. This second parameter is going to be called `animated`. Passing a default value of boolean true is going to help and avoid any explicit mentions. Otherwise you can explicitly pass the value of the parameter as well. ```js export default function useStatusBar(style, animated = true) { useFocusEffect( useCallback(() => { StatusBar.setBarStyle(style, animated); }, []) ); } ``` The animation used by the hook itself is going to the default transition of native platform the app is currently being run since the hook `useFocusEffect` is imported from `@react-navigation/native`. Now you can go back to the simulator (_the below demo is using iOS simulator_) and notice the difference between the previous section and this one. ## A last challenge The purpose of this series is to make yourself familiar with integration process of Firebase in a React Native app and implementing a navigation flow with `react-navigation` library and how to use components from `react-native-paper` UI library. This purpose is now complete with this part. Though I leave you with a small challenge. Implement the logout button in the header bar of the Home screen. Just like you have used the `IconButton` component from `react-native-paper` to open a modal screen. Hint, changes are to be done in the `HomeStack.js` file and we have already written the `logout` function in `AuthProvider.js` file. Here is a little demo showcasing what has to be implement: Try to do it yourself and try to think other ways you can implement log out functionality in this app. If you feel stuck or want to jump ahead to the the GitHub commit [**here**](https://github.com/amandeepmittal/react-native-examples/commit/b1383ccc9fca20214b6c91bfe5a2a5d72a1f8d16). Otherwise, you can find the complete code at this [GitHub repo](https://github.com/amandeepmittal/react-native-examples/tree/master/ChatApp). --- 👉 Here is a list of resources used in this tutorial: - [The complete documentation useFocusEffect hook](https://reactnavigation.org/docs/use-focus-effect/) - [Do understand the difference between using `focus` event and useFocusEffect](https://reactnavigation.org/docs/use-focus-effect/#how-is-usefocuseffect-different-from-adding-a-listener-for-focus-event) [Originally Published at Heartbeat.Fritz.ai](https://heartbeat.fritz.ai/chat-app-with-react-native-part-6-create-a-custom-hook-to-change-status-bar-styles-da7073c5fa8d) --- ## How to clear global npx cache Slug: clear-global-npx-cache Recently, I've seen myself running into the following message whenever I'm using a certain CLI tool with `npx`. ```bash A new version of "x-package" is available You can update by running: npm install -g x-package ``` I don't want to install the CLI tool globally and go into the rabbit hole of maintaining it as a dependency. One way I've found that works on macOS to clear the global `npx` cache: ```bash rm -rf ~/.npm/_npx ``` This will clear the global `npx` cache and you'll be able to use the latest version of the CLI tool. Also, you can add an alias to your `.zshrc` or `.bashrc` file to make it easier to run: ```bash alias clearnpx="rm -rf ~/.npm/_npx" ``` If you've trouble finding where npm stores `npx` cache on your system, run the following command to find out the exact path to the `_npx` directory: ```bash npm config get cache ``` --- ## Common Prop Types in TypeScript and React Slug: common-proptypes-in-react-and-typescript All **primitives in JS** are available in TS. ```ts type Props = { size: number; name: string; disabled: boolean; }; ``` An **object** **type** is simply an empty object or an object with keys. An empty object can have any number of properties and values. If the object is defined explicitly with keys, it will only accept those values. The shape of the object will remain certain. ```ts type Props = { emptyObject: {}; product: { id: string; price: number; }; }; ``` Using square brackets `[]`, an **array type** is defined: ```ts type ListProps = { items: string[]; }; ``` The prop `items` here only expects values in the array of `string` type. To define an array of objects of a certain shape: ```ts type ListProps = { items: { id: string; name: string; price: number; }[]; }; ``` TypeScript does not asks you to define the shape of each object. Although, refactoring `ListProps` as below is valid: ```ts type Item = { id: string; name: string; price: number; }; type ListProps = { item: Item; items: Item[]; }; ``` Using **[union type](https://react-typescript-cheatsheet.netlify.app/docs/basic/troubleshooting/types/#union-types-and-type-guarding)**, certain values for a prop can be described as: ```ts type Button = { variant: 'primary' | 'danger' | 'info'; value: string | number; }; ``` TypeScript cares when it comes to passing arguments on a function. ```ts type Props = { onEventListener: () => void; // some times event listeners do not have return type onChangeText: (title: string) => void; }; ``` On a function, it is possible to define return type as inline type declaration: ```ts function add(x: number, y: number): number { return a + b; } ``` --- ## How to configure ESLint and Prettier in an Expo project Slug: configure-eslint-prettier-expo-project > Make sure to see the official Expo documentation for latest details on using [ESLint](https://docs.expo.dev/guides/using-eslint/) in your React Native project. When writing JavaScript, I spend a good amount of time fixing basic mistakes. Different project files sometimes end up following different syntax and formatting conventions. Using ESLint rescues me from those mistakes. It is a linter for the JavaScript programming language that helps keep the code syntax consistent and match conventions and warns against the possible source of problems. It is written in Node.js. Also, I like to use some specific set of Prettier rules in my projects. ESLint configures well with it. ## Create a new project To create a new React Native project, I use `create-expo-app`: ```shell npx create-expo-app projectName # Navigate inside the project folder cd projectName ``` ## Install ESLint and Prettier dev dependencies After creating a new project, the next step is to install ESLint and Prettier as dev dependencies. The Expo team has been awesome to provide a package called [eslint-config-universe](https://github.com/expo/expo/tree/master/packages/eslint-config-universe) that comes with basic and shared ESLint configuration for Node.js, React Native and web projects. This is useful because I don't have to set up and define the ESLint configuration from scratch. Run the following command in the terminal: ```shell yarn add --dev eslint-config-universe eslint prettier ``` These packages are installed as `devDependencies` since they are only required during the development of the project. ## Configure ESLint Start by creating a new file called `.eslintrc.js` at the root of the project. This file is responsible to contain all the configuration and linting rules. Here is the minimal configuration I use: ```js module.exports = { extends: ['universe', 'universe/native'], rules: { 'import/order': 0, 'react-native/no-inline-styles': 0, 'import/namespace': 0, 'no-duplicate-imports': 'error' } }; ``` Sometimes I extend this configuration or tweak with rules but for most of the projects I start with this configuration. In the above snippet, the `extends` is used to apply the pre-defined set of rules from `universe` and `universe/native`. The `universe` contains basic config JavaScript projects. The `universe/native` contains the config for React Native and Expo projects, with support for React and JSX. ## Configure Prettier Prettier is a code formatter that ensures that all the code files follow a consistent styling. If you are into Web development, chances are you are already using it. Create a new file called `.prettierrc` and inside it add the following (_minimal_) configuration: ```json { "printWidth": 100, "tabWidth": 2, "singleQuote": true, "bracketSameLine": true, "trailingComma": "es5", "arrowParens": "avoid" } ``` ## Ignore files I also created two new files to ignore trivial or other configurable files and folders from both linting and formatting. Both `.eslintignore` and `.prettierignore` have the following snippet: ```shell node_modules/** package.json yarn.lock ios/** android/** assets/** .vscode .expo-shared .prettirrc .eslintrc.js ``` ## Conclusion There are about a dozen ways one can configure ESLint rules. However, in this post, I wanted to share this minimal configuration for my future self. --- ## How to Upload a File with Reactjs and Nodejs Slug: connecting-a-node-js-and-reactjs-example ![cover](https://i.imgur.com/X7ju8yL.jpg) > [Originally this article was published on Zeolearn.com](https://www.zeolearn.com/magazine/connecting-reactjs-frontend-with-nodejs-backend) Uploading Files might seem a task that needs to be conquered especially if you are getting into web development. In this tutorial, simple AJAX based file uploads using Reactjs on front-end and Node.js back-end. This is easy to accomplish with the following technologies since the whole source code will be in one language, JavaScript. In this example, to demonstrate for connecting a Reactjs application with Node.js backend, we will be making the use of a simple file upload example. The topics we will be covering are going to be: - Setting up a Back-end of our app using `express-generator` - Using `create-react-app` to scaffold a front-end Reactjs app - Using `axios` for cross-origin API calls - Handling POST requests on our server - Using `express-fileupload`, a promise based library - Lastly, making a connection between Reactjs and Node.js ### Getting Started We will be starting without back-end first. We will write a server application with necessary configurations required to accept cross-origin requests and uploading files. First, we need to install `express-generator` which is the official and quickest way to start with an Express back-end application. ```shell npm install -g express-generator ``` We will install this module globally from our terminal. After installing this global `npm` module, we have an instance of it named `express` to generate our project structure. ```shell mkdir fileupload-example express server cd server ``` When changing the current directory to the project `express` command just scaffolded, we can observe the following structure and files: To run this backend server on default configuration, we have to install the dependencies mentioned in `package.json` first. ```js npm install npm start ``` Express-generator comes with following dependencies. Some of them are essential to use such as `morgan` and `body-parser` and some we can leave out for this project. ```json "dependencies": { "body-parser": "~1.18.2", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "express": "~4.15.5", "jade": "~1.11.0", "morgan": "~1.9.0", "serve-favicon": "~2.4.5" } ``` I will be adding two more packages for our configurable back-end application to behave in the way we want to. ```shell npm install --save cors express-fileupload ``` `cors` provide a middleware function for Express applications to enable various Cross-Origin Resource Sharing options. CORS is a mechanism that allows restricted resources (in our case, API or AJAX requests) on a web page from another domain. It helps a browser and a server to communicate and can be hosted on separate domains. You will understand it more when you will see it in action. The other module, `express-fileupload` is a bare minimum express middleware function for uploading files. The advantages it has it that it has support for Promises and can handle multiple file uploads. With these two important packages added as dependencies in our project, we can now start by modifying the default Express back-end in `app.js` file. ```js const express = require('express'); const path = require('path'); const favicon = require('serve-favicon'); const logger = require('morgan'); const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const cors = require('cors'); // addition we make const fileUpload = require('express-fileupload'); //addition we make const index = require('./routes/index'); const users = require('./routes/users'); const app = express(); // view engine setup app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'jade'); // uncomment after placing your favicon in /public //app.use(favicon(path.join(__dirname, 'public', 'favicon.ico'))); app.use(logger('dev')); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.use(cookieParser()); // Use CORS and File Upload modules here app.use(cors()); app.use(fileUpload()); app.use('/public', express.static(__dirname + '/public')); app.use('/', index); // catch 404 and forward to error handler app.use(function (req, res, next) { const err = new Error('Not Found'); err.status = 404; next(err); }); // error handler app.use(function (err, req, res, next) { // set locals, only providing error in development res.locals.message = err.message; res.locals.error = req.app.get('env') === 'development' ? err : {}; // render the error page res.status(err.status || 500); res.render('error'); }); module.exports = app; ``` In the above code, you would notice that we made some additions. The first addition we did is to import packages `cors` and `express-fileupload` in `app.js` after other dependencies are loaded. ```js const cors = require('cors'); // addition we make const fileUpload = require('express-fileupload'); //addition we make ``` Then just after other middleware functions, we will instantiate these two newly imported packages. ```js // Use CORS and File Upload modules here app.use(cors()); app.use(fileUpload()); ``` Also, we need to allow data coming from a form. For this, we have to enable `urlencoded` options of `body-parser` module and specify a path as to store the image file coming from the client. ```js app.use(bodyParser.urlencoded({ extended: true })); // below, also change this to app.use('/public', express.static(__dirname + '/public')); ``` With this, we can see if our server is working correctly by running: ```shell npm start ``` If you get the screen below by navigation on port `http://localhost:3000`, it means that our server is running. Before we move to generate our front-end application, we need to change to port for our backend since front-end application generated using `create-react-app` will also be running on port `3000`. Open `bin/www` file and edit: ```js /** * Get port from environment and store in Express. */ // 3000 by default, we change it to 4000 var port = normalizePort(process.env.PORT || '4000'); app.set('port', port); ``` ### Setting up Front-end `create-react-app` is another command line utility that to generate a default Reactjs front-end application. ```shell create-react-app node-react-fileupload-front-end ``` We will also install the required library we are going to use for making API calls to our backend server. ```shell yarn add axios ``` `index.js` is the starting point of our application in the `src/` directory. It registers the render function using `ReactDOM.render()` by mounting `App` component. Components are the building blocks in any Reactjs application. This `App` component comes from `src/App.js`. We will be editing this file in our front-end source code. ### File Upload Form We will be using the HTML `form` element that has an input which provides access to the value, that is the file, using `refs`. `Ref` is a special attribute that can be attached to any component in React. It takes a callback function and this callback will be executed immediately after the component is mounted. It can be also be used on an HTML element and the callback function associated will receive the DOM element as the argument. This way, `ref` can be used to store a reference for that DOM element. That is exactly what we are going to do. ```js class App extends Component { // We will add this part later render() { return (

FileUpload

{ this.uploadInput = ref; }} type="file" />

{ this.fileName = ref; }} type="text" placeholder="Enter the desired name of file" />


Uploaded Image:

img
); } } ``` The `input` element must have the `type="file"` otherwise it would not be able to recognize what type we are using it for. It is similar to the values like `email`, `password`, and so on. The `handleUploadImage` method will take care of the API calls that we need to request to the server. If that call is successful, the local state of our React application will be set to let the user know that the upload was successful. Inside this function, to make the API call, we will be using `axios` library we installed when setting up our front end app. ```js constructor(props) { super(props); this.state = { imageURL: '' }; this.handleUploadImage = this.handleUploadImage.bind(this); } handleUploadImage(ev) { ev.preventDefault(); const data = new FormData(); data.append('file', this.uploadInput.files[0]); data.append('filename', this.fileName.value); fetch('http://localhost:4000/upload', { method: 'POST', body: data }).then(response => { response.json().then(body => { this.setState({ imageURL: `http://localhost:4000/${body.file}` }); }); }); } ``` The FormData object lets you compile a set of key/value pairs to send using XMLHttpRequest. It is primarily intended for use in sending form data but can be used independently from forms in order to transmit keyed data. To build a FormData object, instantiating it then appending fields to it by calling its `append()` method like we did above. Since we are not using any styling, our form looks bare minimum and ugly. But you can go ahead and make it look more professional. For brevity, I am going to keep things simple. I recommend you to always enter a file uname, other wise it will store the file on the with `undefined.jpg` name. ### Updating the server to handle AJAX Request Right now, we do not have in our server code to handle the `POST` request React app makes a request to. We will add the route in our `app.js` in our Express application where the default route is defined. ```js app.post('/upload', (req, res, next) => { // console.log(req); let imageFile = req.files.file; imageFile.mv(`${__dirname}/public/${req.body.filename}.jpg`, err => { if (err) { return res.status(500).send(err); } res.json({ file: `public/${req.body.filename}.jpg` }); console.log(res.json); }); }); ``` ```shell npm start ``` This route gets triggered when a request is made to `/upload/`. The callback associated using the route contain `req`, `res` objects and access to `next`, a standard way of defining a middleware function in an Express application. The `req` object has the file and the filename that was uploaded during form submission from the client application. If any error occurs, we return the 500 server error code. Otherwise we return the path to the actual file and console the `response` object to check if everything is work as we expect it. `.mv` file is promise-based and provided to us by the `express-fileupload` package we installed earlier. Try uploading an image file from the client now. Make sure both the client and server are running from different terminal tabs at this point. If you get a success message like this in your terminal: ```shell POST /upload 200 98.487 ms - 25 GET /public/abc.jpg 200 6.231 ms - 60775 ``` At the same time, the client is requesting to view the file on the front-end with a `GET` HTTP method. That means the route `/upload` from the browser is successfully called and everything is working fine. Once the file is uploaded on the server and it will be sent back to the client to reflect that the user has successfully uploaded the file. You can find the complete code for this example at [**FileUpload-Example**](https://github.com/amandeepmittal/fileupload-example) Github Repository. --- ## Content insets with FlatList in React Native Slug: content-insets-in-flatlist In this quick post, let's explore how to use content insets available within FlatList in React Native to ensure that the content is properly presented behind the header. This post is a continuation of [Header blur effect in Expo Router](/blog/blur-effect-in-header-with-expo-router). ## Current approach: using `useHeaderHeight` hook In React Native apps, `FlatList` is a component that renders a list of items. It is a common component that can be used to render a list of items in a scrollable container. In previous tutorial, `useHeaderHeight` hook was used to get the height of the header and then use it to offset the content for iOS devices. ```tsx import { useHeaderHeight } from '@react-navigation/elements'; export default function HomeScreen() { const headerHeight = useHeaderHeight(); return ( ); } ``` The `headerHeight` is then applied as `paddingTop` to the `FlatList` component's `contentContainerStyle` and it is made available using `@react-navigation/elements` library. There's nothing wrong with the approach of calculating the header height manually using `useHeaderHeight` hook. However, the core list view component in React Native provides a way to handle content insets by passing a couple of props to the `FlatList` component. However, `FlatList`, and more over the underlying `ScrollView` component, has a property called `contentInsetAdjustmentBehavior` that can be used to adjust the content insets of the `FlatList` component. ## Using `contentInsetAdjustmentBehavior` and `automaticallyAdjustContentInsets` Content insets define padding or margins that should be applied to scrollable content to prevent it from being obscured by system elements like the status bar, notches, or navigation bars. Without proper inset management, your content might extend under these UI elements, making parts of it inaccessible. The `FlatList` component uses the same two props that `ScrollView` does: `contentInsetAdjustmentBehavior` and `automaticallyAdjustContentInsets`. The `contentInsetAdjustmentBehavior` prop can be set to `automatic` to automatically adjust the content insets based on the safe area and navigation bars. The `automaticallyAdjustContentInsets` prop can be set to `true` to enable this automatic adjustment. ```tsx Trending Manga
} /> ``` You won't need to use `useHeaderHeight` hook anymore. The `contentInsetAdjustmentBehavior` and `automaticallyAdjustContentInsets` work together to ensure that the content starts at the appropriate position, accounting for the transparent header, without requiring manual height calculations. This change results in the same behavior as the previous approach: --- ## Using Context API with React Native Slug: context-api-react-native-firebase ![cover](https://i.imgur.com/tEzuwkP.png) The React Context API lets you avoid passing props from parent to child at every level of the component tree. Neither you have to unnecessarily increase the complexity of the codebase using state management libraries like Redux. Consuming something like Firebase authentication and storage services with the Context API in a React Native or Expo apps is a great use case to try. In this tutorial, I am going to show you how to setup Firebase email authentication in an Expo app using Context API. Before we get started, please note that I am going to use an Expo project that has: - [navigation setup with `react-navigation` 4.x.x](https://amanhimself.dev/authentication-navigation-flow-in-react-native-apps) - caching local images/assets - [login and signup screen setup with formik and yup](https://amanhimself.dev/build-validate-forms-with-react-native-formik-yup) - [handle different field types in React Native forms with formik and yup](https://amanhimself.dev/handle-different-field-types-in-react-native-forms) You can download the **source code** in its current state from [**this Github repo**](https://github.com/amandeepmittal/expo-firebase/releases/tag/0.5.0) before you begin. After installing the source code, please navigate inside the project directory and install dependencies by running the following command: ```shell yarn install # or npm install ``` ## Table of Contents - Requirements - Add Firebase Config & integrate Firebase SDK - Enable Firestore - Add Context API - Signup with Firebase - Handle Real-time/Server Errors - Login a Firebase user - Add a signout button - Check user auth state for automatic login - Conclusion ## Requirements To follow this tutorial, please make sure you following installed on your local development environment and access to the services mentioned below. - Nodejs (>= `10.x.x`) with npm/yarn installed - expo-cli (>= `3.x.x`), (previously known as create-react-native-app) - Firebase account, free tier will do ## Add Firebase Config & integrate Firebase SDK > If you already know how to obtain Firebase API and storage keys, you can skip this section. Otherwise, you can follow along. Create a new [Firebase project from Firebase Console](https://console.firebase.google.com). ![1](https://i.imgur.com/7TSnVLL.png) Next, fill in the suitable details regarding the Firebase project and click on **Create project** button. ![2](https://i.imgur.com/oXFOQBd.png) You will be re-directed towards the dashboard of the Firebase project. Go to **Project settings** from the sidebar menu and copy the `firebaseConfig` object. It has all the necessary API keys that we need in order to use a Firebase project as the backend for any React Native or Expo app. ![3](https://i.imgur.com/XbVjdkB.png) Next, go inside the [Expo app](https://github.com/amandeepmittal/expo-firebase/releases/tag/0.5.0) and create a new directory called `config`. This folder will contain all the configuration files. Inside it, create `Firebase/firebaseConfig.js` file and paste the contents of the config object as below. ```js // Replace all Xs with real Firebase API keys export default { apiKey: 'XXXX', authDomain: 'XXXX', databaseURL: 'XXXX', projectId: 'XXXX', storageBucket: 'XXXX', messagingSenderId: 'XXXX', appId: 'XXXX' }; ``` Next, from the terminal window, install Firebase SDK. ```shell yarn add firebase ``` Back to the `config/Firebase/` directory. Create a new file `firebase.js`. This will hold all the configuration related to integrate the Firebase SDK and the function it provides for authentication, real time database and so on. Also, define a `Firebase` object with some initial methods that you are going to use in the tutorial. These methods are going to conduct real-time events such as user authentication, sign out from the app, and store the user details based on the reference to `uid` (_unique user id Firebase creates for every registered user_) in real-time NoSQL database called **Cloud Firestore**. ```js import * as firebase from 'firebase'; import 'firebase/auth'; import 'firebase/firestore'; import firebaseConfig from './firebaseConfig'; // Initialize Firebase firebase.initializeApp(firebaseConfig); const Firebase = { // auth loginWithEmail: (email, password) => { return firebase.auth().signInWithEmailAndPassword(email, password); }, signupWithEmail: (email, password) => { return firebase.auth().createUserWithEmailAndPassword(email, password); }, signOut: () => { return firebase.auth().signOut(); }, checkUserAuth: user => { return firebase.auth().onAuthStateChanged(user); }, // firestore createNewUser: userData => { return firebase .firestore() .collection('users') .doc(`${userData.uid}`) .set(userData); } }; export default Firebase; ``` This approach used with React's Context API will eliminate the use of Redux state management (which is the approach I worked with [previously](https://amanhimself.dev/how-to-build-an-email-authentication-app-with-firebase-firestore-and-react-native)) library and simply use React principles. Populating the `Firebase` object with Context, you will be able to access all the functions as well as the user throughout this React Native app as props. ## Enable Firestore There are two types of cloud-based database services provided by Firebase. One is called Cloud Firestore, and the other one is known as Realtime Database. Realtime Database stores data as one large JSON tree. Complex and scalable data is hard to organize in it. Cloud Firestore follows proper NoSQL terminology when it comes to storing data. It stores data in documents, and each document can have sub-collections—thus, making it suitable for scalable and complex data scenarios. Go back to the Firebase console and in the Database section, choose the Cloud Firestore and click on the button **Create database**. ![4](https://i.imgur.com/k7Ecql7.png) Then, choose the option Start in **test mode** and click the button **Next** as shown below. ![5](https://i.imgur.com/jLWPy9K.png) ## Add Context API The common reason to use Context API in a React Native app is that you need to share some data in different places or components in the component tree. Manually passing props can be tedious as well as hard to keep track of. The Context API consists of three building blocks: - creating a context object - declaring a provider that gives the value - declaring a consumer that allows a value to be consumed (_provided by the provider_) Create a new file inside the `Firebase` directory called `context.js`. Declare a `FirebaseContext` that is going to be an object. ```js import React, { createContext } from 'react'; const FirebaseContext = createContext({}); ``` After creating the context, the next step is to declare a provider and a consumer. ```js export const FirebaseProvider = FirebaseContext.Provider; export const FirebaseConsumer = FirebaseContext.Consumer; ``` Lastly, let us declare an HoC (_High Order Component_) to generalize this Firebase Context. An HoC in React is a function that takes a component and returns another component. What this HoC will do is instead of importing and using `Firebase.Consumer` in every component necessary, all there is to be done is just pass the component as the argument to the following HoC. ```js export const withFirebaseHOC = Component => props => ( {state => } ); ``` You will understand with more clarity in the next section when modifying the existing `Login` and `Signup` component with this HoC. Now, create a new file `index.js` to export both the `Firebase` object from the `firebase.js` file, the provider and the HoC. ```js import Firebase from './firebase'; import { FirebaseProvider, withFirebaseHOC } from './context'; export default Firebase; export { FirebaseProvider, withFirebaseHOC }; ``` The provider has to grab the value from the context object for the consumer to use that value. This is going to be done in `App.js` file. The value for the `FirebaseProvider` is going to be the `Firebase` object with different strategies and functions to authenticate and store the user data in real-time database. Wrap the `AppContainer` with it. ```js import React from 'react'; import AppContainer from './navigation'; import Firebase, { FirebaseProvider } from './config/Firebase'; export default function App() { return ( ); } ``` That's it for setting up the Firebase SDK. ## Signup with Firebase In this section, you are going to modify the existing `Signup.js` component in order to register a new user with the firebase backend and store their data in Firestore. To start, import the `withFirebaseHOC`. ```js import { withFirebaseHOC } from '../config/Firebase'; ``` Replace the `handleSubmit()` method with `handleOnSignup()`. Since all the input values are coming from Formik, you have to edit `onSubmit` prop on the `Formik` element too. The `signupWithEmail` is coming from firebase props and since you are already wrapping the navigation container with `FirebaseProvider`, `this.props.firebase` will make sure any method inside the `Firebase` object in the file `config/Firebase/firebase.js` is available to be used in this component. The `signupWithEmail` method takes two arguments, `email` and `password` and using them, it creates a new user and saves their credentials. It then fetches the user id (_`uid`_) from the response when creating the new user. The `createNewUser()` method stores the user object `userData` inside the collection `users`. This user object contains the `uid` from the authentication response, the name, and email of the user entered in the signup form. ```js handleOnSignup = async values => { const { name, email, password } = values try { const response = await this.props.firebase.signupWithEmail( email, password ) if (response.user.uid) { const { uid } = response.user const userData = { email, name, uid } await this.props.firebase.createNewUser(userData) this.props.navigation.navigate('App') } } catch (error) { console.error(error) } } // replace with handleOnSignup onSubmit={values => { this.handleOnSignup(values) }} ``` The logic behind saving the user object is the following: ```js // config/Firebase/firebase.js createNewUser: userData => { return firebase .firestore() .collection('users') .doc(`${userData.uid}`) .set(userData); }; ``` Lastly, do not forget to export the `Signup` component inside the `withFirebaseHOC`. ```js export default withFirebaseHOC(Signup); ``` Let see how it works. ![f1](https://i.imgur.com/r40CEuW.gif) Since it is going to the Home screen, means that use is getting registered. To verify this, visit the Database section from Firebase Console Dashboard. You will find a `users` collection have one document with the `uid`. ![6](https://i.imgur.com/Q1aoXx2.png) To verify the `uid`, visit **Authentication** section. ![7](https://i.imgur.com/QXX3tXM.png) ## Handle Real-time/Server Errors To handle real-time or server errors, Formik has a solution to this. Now, understand that something valid on the client-side can be invalid on the server. Such as, when registering a new user with an already existing email in the Firebase storage should notify the user on the client-side by throwing an error. To handle this, edit the `onSubmit` prop at the `Formik` element bypassing the second argument called `actions`. ```js onSubmit={(values, actions) => { this.handleOnSignup(values, actions) }} ``` Next, instead of just console logging the error values, to display the error, you will have to use `setFieldError`. This will set an error message in the `catch` block. Also, add a `finally` block that will avoid the form to submit in case of an error. ```js handleOnSignup = async (values, actions) => { const { name, email, password } = values; try { const response = await this.props.firebase.signupWithEmail(email, password); if (response.user.uid) { const { uid } = response.user; const userData = { email, name, uid }; await this.props.firebase.createNewUser(userData); this.props.navigation.navigate('App'); } } catch (error) { // console.error(error) actions.setFieldError('general', error.message); } finally { actions.setSubmitting(false); } }; ``` Lastly, do display the error on the app screen, add an `ErrorMessage` just after the `FormButton` component. ```js ``` Now go back to the Signup form in the app and try registering the user with the same email id used in the previous step. ![f2](https://i.imgur.com/XXK3D7N.gif) _Voila!_ It works! The error message is shown and it does not submit the form. ## Login a Firebase user As the previous section, similar number of steps have to be performed for the Login form to work. Instead of going through them individually, here is the complete `Login` component. ```js import React, { Component, Fragment } from 'react'; import { StyleSheet, SafeAreaView, View, TouchableOpacity } from 'react-native'; import { Button } from 'react-native-elements'; import { Ionicons } from '@expo/vector-icons'; import { Formik } from 'formik'; import * as Yup from 'yup'; import { HideWithKeyboard } from 'react-native-hide-with-keyboard'; import FormInput from '../components/FormInput'; import FormButton from '../components/FormButton'; import ErrorMessage from '../components/ErrorMessage'; import AppLogo from '../components/AppLogo'; import { withFirebaseHOC } from '../config/Firebase'; const validationSchema = Yup.object().shape({ email: Yup.string() .label('Email') .email('Enter a valid email') .required('Please enter a registered email'), password: Yup.string() .label('Password') .required() .min(6, 'Password must have at least 6 characters ') }); class Login extends Component { state = { passwordVisibility: true, rightIcon: 'ios-eye' }; goToSignup = () => this.props.navigation.navigate('Signup'); handlePasswordVisibility = () => { this.setState(prevState => ({ rightIcon: prevState.rightIcon === 'ios-eye' ? 'ios-eye-off' : 'ios-eye', passwordVisibility: !prevState.passwordVisibility })); }; handleOnLogin = async (values, actions) => { const { email, password } = values; try { const response = await this.props.firebase.loginWithEmail( email, password ); if (response.user) { this.props.navigation.navigate('App'); } } catch (error) { actions.setFieldError('general', error.message); } finally { actions.setSubmitting(false); } }; render() { const { passwordVisibility, rightIcon } = this.state; return ( { this.handleOnLogin(values, actions); }} validationSchema={validationSchema} > {({ handleChange, values, handleSubmit, errors, isValid, touched, handleBlur, isSubmitting }) => ( } /> )} ) : ( )} ); } } ``` Now, a question you may ask, how come `this.props.theme` & `this.props.switchTheme` are available to the above component. In `App.js`, which is the parent component for `HomeScreen`, is not passing any props down the component tree. Well, from the previous import statements, you are importing two important Redux methods: `bindActionCreators` and `connect`. The bindActionCreators maps actions to an object using the names of the action functions. These functions automatically dispatch the action to the store when the function is invoked. As we learned earlier, to change the data, we need to dispatch an action. To enable this, you further need two things: `mapStateToProps` and `mapDispatchToProps`. You have to connect both of them with `HomeScreen` component. This connection is done by using the `connect()` method from the `react-redux` package which connects the current React Native component to the Redux store. Add the following at the end of component file: ```js const mapStateToProps = state => ({ theme: state.themeReducer.theme }); const mapDispatchToProps = dispatch => ({ switchTheme: bindActionCreators(switchTheme, dispatch) }); export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen); ``` ## Using Props in styled-components By passing an interpolated function `${props => props...}` to a styled component's template literal you can extend that component's styles. Take a look at the following code snippet, and modify the styles wherever necessary. ```js const Container = styled.SafeAreaView` flex: 1; background-color: ${props => props.theme.PRIMARY_BACKGROUND_COLOR}; justify-content: center; align-items: center; `; const TextContainer = styled.View` padding: 15px; border-radius: 5px; border: 1px solid ${props => props.theme.PRIMARY_TEXT_COLOR}; `; const Title = styled.Text` padding: 20px; font-size: 24px; font-weight: 500; color: ${props => props.theme.PRIMARY_TEXT_COLOR}; `; const Button = styled.TouchableOpacity` margin-top: 20px; background-color: ${props => props.theme.SECONDARY_BUTTON_COLOR}; border-radius: 5px; padding: 10px; `; const ButtonText = styled.Text` font-size: 20px; color: ${props => props.theme.SECONDARY_TEXT_COLOR}; `; ``` Now, go to the simulator running and you will notice a new button with a text that says `Switch to ...` name of the next theme. If you have been following this tutorial, you will notice that the initial or current theme is the light mode. By pressing the button, you can switch to the dark mode. ![ss2](https://i.imgur.com/W6offhJ.gif) ## Conclusion _Congratulations!_ You have successfully integrated redux and styled-components in a React Native app to create style attributes for React Native and manage themes. Using `props` in styled-components you learned how to manage and write composable components. This is just one of the way to create a themeable React Native app. To dwell more into styled-components, please refer to the official documentation [**here**](https://www.styled-components.com/docs/basics#react-native). --- ## How to create custom wavy headers with react-native-svg Slug: create-custom-headers-with-react-native-svg > [Originally Published at Heartbeat.Fritz.ai](https://heartbeat.fritz.ai/creating-custom-wavy-headers-using-react-native-svg-639ce0861327) In React Native apps, the support for SVG graphics is provided by an open-source module called [`react-native-svg`](https://github.com/react-native-community/react-native-svg) that is maintained by React Native community. Using SVG can enhance an app’s design when it comes to displaying different patterns. It can make a difference in how the look and feel of the app might appear to the end-user, as well how it is easy to edit the pattern built using SVG. SVG is mainly found on the web, and while they have similar uses to JPEG, PNG, and WebP image types, SVG is not resolution-dependent. Hence, the definition according to [Wikipedia](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics):
Scalable Vector Graphics (SVG) is an Extensible Markup Language (XML)-based vector image format for two-dimensional graphics with support for interactivity and animation.
This format consists of shapes rather than pixels which can further be concluded that an SVG graphic can be scaled indefinitely in terms of resolution. In this post, let us learn how to use `react-native-svg` in React Native and Expo apps and create some custom examples such as wavy header shown below. ## Requirements Ensure your dev environment includes the following required packages: - [Node.js](https://nodejs.org/) above `12.x.x` installed on your local machine - JavaScript/ES6 basics - [`expo-cli`](https://expo.io/tools) ## Installing react-native-svg library Start by creating a new project using expo-cli. Navigate inside the project directory when the CLI has finished generating the new project. Then install all the required dependencies to integrate the `react-native-svg` library. ```shell expo init [PROJECT NAME] cd [PROJECT NAME] expo install react-native-svg ``` The reason to use `expo install` command when building a React Native app using Expo SDK instead of package managers like `npm` or `yarn` is that it is going to install the most compatible version of the package available to be used with Expo SDK. This avoids unnecessary errors. That's it for installing this library. The `react-native-svg` library contains common shapes and elements such as `Svg`, `Rect`, `Circle`, `Line`, `Polygon`, `Path`, and so on as components to be used. You are going to see `Svg` and `Path` in action, in this post. ## Create a header component In the next few sections, let us try to create a custom header background that has a bottom border with the form of a wave as shown below. Start by creating a new screen component inside `src/screens/ScreenOne.js` file that displays a heading on the screen. (_Create the directory if it doesn't exist._) Add the following code snippet to this file. ```js import React from 'react'; import { StyleSheet, View, Text, Dimensions } from 'react-native'; export default function ScreenOne() { return ( Custom Header ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff' }, headerContainer: { marginTop: 50, marginHorizontal: 10 }, headerText: { fontSize: 30, fontWeight: 'bold', color: '#333', textAlign: 'center', marginTop: 35 } }); ``` Next, go to `App.js` file and modify it to render the `ScreenOne` functional component as below. ```js import React from 'react'; import ScreenOne from './src/screens/ScreenOne'; import { StatusBar } from 'react-native'; export default function App() { return ( <>