Latest Tutorials

Learn about the latest technologies from fellow newline community members!

  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL
  • React
  • Angular
  • Vue
  • Svelte
  • NextJS
  • Redux
  • Apollo
  • Storybook
  • D3
  • Testing Library
  • JavaScript
  • TypeScript
  • Node.js
  • Deno
  • Rust
  • Python
  • GraphQL

    State Management in React Native with Redux and Redux Thunk

    The biggest challenge you will face when building a React Native application is managing state predictably across every component. With so many available NPM libraries that address the problem of state management, such as Redux and MobX , and built-in APIs for features like React Context , architecting a robust state management solution has never been easier. However, if we don't fully understand how these technologies work, then we may end up littering the application state with too much state and triggering unnecessary re-renders from extra state changes. The key to state management is distinguishing between two types of state: component and application. Take any one component from your application, and for each state variable, ask yourself whether it belongs only within this component or if at least one other component depends on it. State isolated to a single component should be handled with the useState hook or passed from a parent component via props , whereas state shared by multiple components should be handled by React Context or a third-party library like Redux. While you can manage state on the native side, the transmission of data across the React Native bridge and serialization/deserialization of data per operation is neither optimal nor worthwhile. You should just keep the application state on the JavaScript side. Proper state management accomplishes a few things for the codebase: Despite the React Context API, the first-party, built-in, lightweight state management solution, you shouldn't immediately write off Redux for managing application state. You may reject Redux because of the additional boilerplate code it adds to your application, but Redux enforces a unidirectional flow of data and strict patterns for manipulating application state. In a team environment, Redux provides a superior developer experience with time-travel debugging tools ( Redux DevTools ) for finding bugs earlier in development and state snapshots for quickly reproducing bugs. For React Native applications, Redux middleware lets us execute side-effects, such as Google Analytics event tracking and asynchronous API calls, without blocking state updates. Middleware extends Redux's capabilities, and they can be chained together to perform complex asynchronous logic and interactions. Writing middleware is easy with Redux Thunk , which dispatches actions asynchronously by delaying the dispatch with thunks. Typically, an action creator looks like this: When called, the action creator returns an action object. The action can be dispatched synchronously to a Redux store's reducer like so: With thunks , the action creator returns a function that performs the dispatch asynchronously. For example, what if we needed to persist some data to a Redis cache prior to updating the Redux store? Sending a request to an API endpoint that caches data in a Redis instance is an asynchronous action. By wrapping code within a function to delay its immediate invocation, thunks elegantly runs asynchronous logic while still being able to interact with the store. Best of all, we can dispatch thunk-based actions as if they were standard actions! Below, I'm going to show you how to integrate Redux and Redux Thunk into a React Native application from scratch. To get started, initialize a new React Native project using the TypeScript template : Within the root of the project directory, install the Redux ( redux ) and Redux Thunk ( redux-thunk ) libraries and their type definitions from the npm registry by running these commands: react-redux provides React bindings for Redux, and redux-logger logs Redux actions and snapshots the state before and after the action is dispatched and handled by the reducer. Now, create the following directories: Anytime you introduce a new component that interacts with the Redux store, structure the component in the following way: This approach loosely couples Redux with the component. Since the component receives the data and methods via its props, they can hypothetically come from anywhere. If you decide to remove Redux from your application or move the component to its own library (or to an application without Redux), then the component will still work properly as long as it receives the data and methods from a parent component. Let me show you how this works with an example. Suppose we add a <Counter /> component that displays the current count stored in the Redux store and features two buttons for incrementing and decrementing the count by one. Nothing fancy. Start by writing the store, reducers and action creators. To initialize a new store, call Redux's createStore function. ( shared/redux/store/index.ts ) Although redux-logger is production-ready, the overhead of logging on each action may adversely affect performance based on the number of logs being printed, the size of each log statement, etc. We'll limit logging to development only. Hence, the store will only apply the redux-logger middleware during development. Note : You can configure redux-logger to log certain actions or collapse actions that didn't result in an error. Visit the redux-logger GitHub repository and read the documentation for more information. Only a single set of reducers will be added to the store: the counter reducer. It will modify the portion of the state tree that the <Counter /> component uses. ( shared/redux/reducers/index.ts ) Anytime an action of type INCREMENT or DECREMENT is dispatched, the counter reducer will either increment or decrement the stored count by one. Make sure that all operations involving state inside the reducer are immutable for time-travel debugging. ( shared/redux/reducers/counter.ts ) Define two action creators for the INCREMENT and DECREMENT actions: ( shared/redux/constants/index.ts ) ( shared/redux/actions/index.ts ) The <Counter /> component increments and decrements the value of count stored within the Redux store. Connect the component to the Redux store by calling Redux's connect function and passing it mapStateToProps and mapDispatchToProps . mapStateToProps determines what part of the data from the store should be passed to the component. In this case, the count value from state.counter will be passed to the component as the prop count . mapDispatchToProps determines the actions the component can dispatch. In this case, the component should be able to dispatch the INCREMENT and DECREMENT actions. The INCREMENT action will be dispatched when the component calls the onIncrement prop, and the DECREMENT action will be dispatched when the component calls the onDecrement prop. ( src/components/counter/index.ts ) Inside of the <Counter /> component, display the current count's value within a <Text /> component and assign the onIncrement and onDecrement functions to the "+ Increment" and "- Decrement" buttons' onPress respectively. Pressing on any of these buttons will either increment or decrement the count. For example, calling onIncrement dispatches the INCREMENT action, which gets handled by the counter reducer and updates count in the store to count + 1 . ( src/components/counter/Component.tsx ) Ignoring the src/components/counter/index.ts file, you could easily move this <Counter /> component to a different codebase that doesn't depend on Redux, and it would work perfectly fine as long as you provide the same set of props: Finally, wrap the part of the application that interacts with the Redux store within a <Provider /> component. ( App.tsx ) Try it out in the iOS simulator by running the following command: Notice how the redux-logger middleware, much like middleware being executed on every incoming request in Express.js , gets executed on every dispatched action. Redux Thunk serves as syntactic sugar for asynchronous actions. It eliminates the requirement of passing the dispatch function to the action creator by having the action creator return an anonymous function that automatically provides the dispatch function. To support thunks, add Redux Thunk to the Redux store's middleware: ( shared/redux/store/index.ts ) If we added a save button that sends a request to a remote API to store the current count inside of a database, then we would have to write the action creator for this functionality as a thunk. For the purposes of this tutorial, we will simulate an API request via a setTimeout . After 300 milliseconds have elapsed (approximately the average response time for an API endpoint), dispatch an action that switches an isSaved flag within the Redux store to true . ( shared/redux/thunk/index.ts ) ( shared/redux/actions/index.ts ) ( shared/redux/constants/index.ts ) ( shared/redux/reducers/counter.ts ) Whenever the user increments or decrements the current count, the isSaved flag resets to false because the count has changed. This new value hasn't been saved yet. Like onIncrement and onDecrement within mapDispatchToProps , onSave will follow the same syntax. The only exception is that onSave dispatches a thunk that dispatches a standard action after completing some asynchronous work. ( src/components/counter/index.ts ) Lastly, add the save button to the <Counter /> component and display status text that shows "Saved!" whenever the user presses the save button. ( src/components/counter/Component.tsx ) For a final version of this demo application, check out the source code at this GitHub repository . Reload the application and try out the new save button! Check out Amit Mangal 's React Native starter kit with TypeScript, Redux, Redux Thunk and React Native Navigation for a live, in-depth example on how to properly integrate Redux into a React Native project. Next time, try adding Redux and Redux Thunk into your next React Native project!

    Thumbnail Image of Tutorial State Management in React Native with Redux and Redux Thunk

    Which Module Formats Should Your JavaScript Library Support?

    As a web application grows and more features are added, modularizing the code improves readability and maintainability. In a basic web application, the application fetches and loads JavaScript files by having multiple <script /> tags in an HTML document. Often, these <script /> tags reference libraries from CDNs, such as cdnjs or unpkg , before the bundled application code. This approach involves manually ordering the <script /> tags correctly. For example, if you wanted to use a library like React in the browser (without any additional tooling, just plain HTML, CSS and JavaScript), then you would need to... Continually adding more JavaScript files becomes more tricky because not only do you need to ensure that their dependencies precede them, but also that there are no naming collisions as a result of variables sharing the same global scope and overriding each other. With no native, built-in features for namespacing and modules in early versions of JavaScript language, different module formats have been introduced over the years to fill this void until the ES6 specification, which includes official syntax for writing modules via ECMAScript (ES6) modules. Below, I'm going to provide a brief overview of the following module systems: For developers, understanding each module system allows them to better determine which one best suits their applications' needs. For library authors, choosing the module systems their library should be compatible with depends on the environments where their intended users' applications run. One of the earliest ways of exposing libraries in the web browser, immediately invoked function expression (IIFE) are anonymous functions that run right after being defined. Variables declared within the IIFE are scoped to the anonymous function, not to the global scope. This keeps variables inaccessible from outside the IIFE (restricts access only to the code within the IIFE) and prevents the pollution of the global scope. A common design pattern that leverages IIFEs is the Singleton pattern , which creates a single object instance and namespaces code. This object serves as a single point of access to a specific group of functionality. For real-world examples, look no further than the Math object or jQuery . For example... Writing modules this way is convenient and supported by older browsers. In fact, you can safely concatenate and bundle together multiple files that contain IIFEs without worrying about naming and scoping collisions. For modules with dependencies, you can pass those dependencies as arguments to the IIFE's anonymous function. If the dependency is of a primitive data type, then any changes made to the dependency in the global scope will not affect the IIFE module. If the dependency is an object, then any changes made to the dependency's properties in the global scope will affect the IIFE module, unless you destructure out the properties used in the IIFE module. For the previous example, if we override the Math object's pow method after running the IIFE, then subsequent calls to the module's calculateMethod method will return a different value. If we modify the IIFE module to accept the Math object as a dependency argument, then the module "captures a snapshot" of the object's properties via destructuring. Overriding these properties' values in later statements will have no affect on the module's methods. Nevertheless, IIFE modules load synchronously, which means correctly ordering the module files is critical. Otherwise, the application will break. For large projects, IIFE modules can be difficult to manage, especially if you have many overlapping and nested dependencies. The default module system of Node.js , CommonJS (CJS) uses the require syntax for importing modules and the module.exports and exports syntax for default and named exports respectively. Each file represents a module, and all variables local to the module are private since Node.js wraps the module within a function wrapper. For example, this module... Becomes... The module not only has its variables scoped privately to it, but it still has access to the globals exports , require and module . __filename and __dirname are module scoped and hold the file name and directory name of the module respectively. The require syntax lets you import built-in Node.js or third-party modules locally installed and referenced in package.json (if supplied a module name) or custom modules (if supplied a relative path). ( ./circle.js ) ( Some File in the Same Directory as ./circle.js ) CommonJS require statements are synchronous, which means CommonJS modules are loaded synchronously. Provided the application's single entry point, CommonJS automatically knows how to order the modules and dependencies and handle circular dependencies. If you decide to use CommonJS modules for your client-side applications, then you need additional tooling, such as Browserify , RollupJS or Webpack , to bundle and transpile the modules into a single file that can be understood and ran by a browser. Remember, CommonJS is not part of the official JavaScript specification. Much like IIFEs, CommonJS was not designed for generating small bundle sizes. Bundle size was not factored into CommonJS's design since CommonJS is mostly used for developing server-side applications. For client-side applications, the code must be downloaded first before running it. As a result, larger bundle sizes serve as performance bottlenecks that slow down an application. Plus, without tree-shaking, this makes CommonJS a non-optimal module system for client-side applications. Unlike IIFEs and CommonJS, Asynchronous Module Definition (AMD) asynchronously loads modules and their dependencies. Originating from the Dojo Toolkit, AMD is designed for client-side applications and doesn't require any additional tooling. In fact, all you need to run applications following the AMD module format is the RequireJS library, an in-browser module loader. That's it. Here's a simple example that runs a simple React application, structured with AMD, in the browser. ( index.html ) ( main.js ) Calling the requirejs or define methods registers the factory function (the anonymous function passed as the second argument to these methods). AMD executes this function only after all the dependencies have been loaded and executed. Each module references dependencies by name, not by their global variable. Each dependency name is mapped to the location of the dependency's source code. AMD allows multiple modules to be defined within a single file, and it supports older browsers. However, it is not as popular as more modern module formats, such as ECMAScript modules and Universal Module Definition. For libraries that support both client-side and server-side environments, the Universal Module Definition (UMD) offers a unified solution for making modules compatible with many different module formats, such as CommonJS and AMD. Regardless of whether an application consumes your library as a CommonJS, AMD or IIFE module, UMD conditionally checks for the module format being used at runtime and executes code specific to the detected module format. The UMD template code is an intimidating-looking IIFE, but it is not difficult to understand. Here's UMD in action from React 's development library. Here, the factory function contains the actual library code. ECMAScript modules (ESM), the most recently introduced and recommended module format, is the standard and official way of handling modules in JavaScript. Module bundlers support ESM and optimize the code using techniques like tree-shaking (removes unused code from the final output), which are not supported by other module formats. Loading and parsing modules is asynchronous, but executing them is synchronous. This module format is commonly used in TypeScript applications. Similar to CommonJS, ESM provides multiple ways for exporting code: default exports or named exports. A file can only have one default export ( default export syntax), but it can have any number of named exports ( export function , export const , etc. syntax). ( ./circle.js ) Importing these named exports separately tells the module bundler which portions of the imported module should be included in the outputted code. Any named exports not imported are omitted. This decreases the size of the library, which is useful if your library relies on a few methods from a large utility library like lodash . ( Some File in the Same Directory as ./circle.js ) Ask yourself the following questions while building your library: Once you answer these questions, you can tell the module bundler to output the code in specific module formats for users to consume it within their own libraries/projects. You can also check out our new course, The newline Guide to Creating React Libraries from Scratch , where we teach you everything you need to know to succeed in creating your own library. 

    Thumbnail Image of Tutorial Which Module Formats Should Your JavaScript Library Support?

    I got a job offer, thanks in a big part to your teaching. They sent a test as part of the interview process, and this was a huge help to implement my own Node server.

    This has been a really good investment!

    Advance your career with newline Pro.

    Only $30 per month for unlimited access to over 60+ books, guides and courses!

    Learn More

    Why should I care about abstract syntax trees as a frontend engineer?

    You may have seen the term "abstract syntax trees", "AST", or maybe even learned about them in a computer science curriculum, but chalked them up as being irrelevant to the work you need to do as a frontend engineer. On the contrary, abstract syntax trees are ubiquitous in the frontend ecosystem. Understanding abstract syntax trees isn't a requirement for being a productive or successful frontend engineer, but it can unlock a new skill set that has many practical applications in frontend development. First, what are abstract syntax trees? In the simplest form, an abstract syntax tree is a way to represent code so a computer can understand it. The code we write is a giant string of characters that will only do something if a computer can understand and interpret the code. An abstract syntax tree is a  tree data structure . A tree data structure starts with a root value. The root can then point to other values, and those values to others, and so on. This begins to create an implicit hierarchy, and also happens to be a great way to represent source code in a way computers can easily interpret. For example, say we had the code snippet  2 + (4 * 10) . To evaluate this code, multiplication is performed first, followed by addition. Since the addition is the last thing to happen or the highest in this hierarchy it will be the root. It then points to two other values, on the left is the number 2 , but on the right is another equation. This time it's multiplication, and it also has two values, the number  4  on the left and 10  on the right. A common example of using abstract syntax trees is in compilers. A compiler accepts source code as input, and then outputs another language. This is often from a high-level programming language to something low-level, like machine code. A common example in frontend development is transpilation, where modern JavaScript is transpiled to an older version of JavaScript syntax. But how does this impact you as a frontend engineer? First, you likely rely on tooling built on top of abstract syntax trees on a daily basis. Some common examples of frontend build tools or compilers that rely on abstract syntax trees are webpack ,  babel , and swc. However, they aren't isolated to build tools. Tools like Prettier (code formatter) , ESLint (code linter) , or jscodeshift (code transformer) have different purposes but they all rely on abstract syntax trees since they all need to understand and work with source code directly. It's possible to use most of these tools without an understanding of abstract syntax trees, but some have an expectation that you understand ASTs for the more advanced uses. For example, to create a custom linting rule with ESLint, or a custom code transform with jscodeshift requires traversing and mutating ASTs. Trying to use these tools without this understanding can be a challenging and confusing experience. One of the biggest benefits is using tools like babel or swc directly to generate, traverse, and mutate ASTs. This enables interacting with code in a reliable and automated way and many of the tools mentioned earlier use these or equivalent tools internally. This allows creating completely custom functionality to statically analyze/audit code, make dynamic code transformations, or whatever problem you might be solving in a large codebase. While it's not necessary to understand abstract syntax trees to be a productive and successful frontend engineer, having a basic understanding can uplevel your ability to maintain continuously evolving large codebases and more easily interact with common tools that rely on them.  Abstract syntax trees enable interacting with a codebase "at scale." For more on how to make sweeping changes in a safe and reliable way in any size codebase, check out our course  The newline Guide to Practical Abstract Syntax Trees.

    Thumbnail Image of Tutorial Why should I care about abstract syntax trees as a frontend engineer?

    The newline Guide to Practical Abstract Syntax Trees is Now Live! 🎉

    Learn the practical techniques you need today to modify any large-scale codebase without the hassle of manual, one line at a time refactors.  We use real world tools such as Facebook's jscodeshift to apply these powerful concepts on actual codebases. The course goes beyond just theory to practical hands on coding , with a sample codebase we provide that you will modify as you apply the techniques you learn in the course. With  Practical Abstract Syntax Trees  you  unlock the ability to make sweeping changes in a safe and reliable way in any size codebase . We'll tackle:  It's taught by Spencer Miskoviak, who's an engineer at WealthFront, the leading automated investing services firm with over $20 billion in assets under management (AUM). Spencer is a recognized expert on ASTs and JavaScript. He presented on ASTs at React Conf in 2019, showing advanced ways to optimize a JavaScript codebase. Access The newline Guide to Practical Abstract Syntax Trees for all of the practical techniques you need to maintain any size codebase.

    Thumbnail Image of Tutorial The newline Guide to Practical Abstract Syntax Trees is Now Live! 🎉

    NPM: What are project dependencies?

    Code dependencies are like Lego's . We're able to pull in other people's code; combining and stacking different packages together to fulfill our goals. Using dependencies greatly reduces the complexity of developing software. We can take advantage of the hard work someone has already done to solve a problem so we can continue to build the projects we want. A development pipeline can have multiple kinds of code dependencies: In JavaScript, we have a package.json file that holds metadata about our project. package.json can store things like our project name, the version of our project, and any dependencies our project has. Dependencies, devDependencies, and peerDependencies are properties that can be included in a package.json file. Depending on the instance where code will be used changes the type of dependency a package is. There are packages that our users will need to run our code. A user is someone not directly working in our code-base. This could mean a person interacting with an application we wrote, or a developer writing a completely separate library. In other words, this is a production environment. Alternatively, there are packages that a developer or system only needs while working in our code. For example linters, testing frameworks, build tools, etc. Packages that a user won't need, but a developer or build system will need. Dependencies are packages our project uses in production . These get included with our code and are vital for making our application run. Whenever we install a dependency the package and any of its dependencies get downloaded onto our local hard drive. The more dependencies we add, the bigger our production code becomes. This is because each new dependency gets included in the production build of our code. Evaluate adding new dependencies unless they're needed! Dependencies are installed using npm install X or yarn add X Packages needed in development , or while developing our code, are considered dev dependencies. These are programs, libraries, and tools that assist in our development workflow. Dev dependencies also get downloaded to your local hard drive when installed, but the user will never see these dependencies. So adding a lot of dev dependencies only affects the initial yarn or npm install completion time. Dev Dependencies are installed using npm install --save-dev X or yarn add --dev X Peer dependencies are similar to dependencies except for a few key features. First, when installing a peer dependency it doesn't get added to your node_modules/ directory on your local hard drive. Why is that? Well, peer dependencies are dependencies that are needed in production , but we expect the user of our code to provide the package. The package doesn't get included in our code. This is to reduce including multiples of the same dependency in production . If every React library included a version of React as a dependency, then in production our users would download React multiple times. Peer dependencies are a tool for library owners to optimize their project size. Peer Dependencies are installed using yarn add --peer X I recently released a course, Creating React Libraries from Scratch, where we walk through content just like how NPM dependencies work, and a lot more! We start with an empty project and end the course with a fully managed library on npm. To learn more click the link below! Click to view Creating React Libraries from Scratch!

    Thumbnail Image of Tutorial NPM: What are project dependencies?