Adding TypeScript to Existing React Project

Installation

First install it into the project:

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

or

yarn add typescript @types/node @types/react @types/react-dom @types/jest

Next, rename any file to be a TypeScript file (e.g. src/index.js to src/index.tsx). Type errors will start to show up.

Generate tsconfig.json file

Although the React documentation says that you are not required to make a tsconfig.json file because one will be made automatically for you, that was not my experience. After doing the installation, a tsconfig.json file was not generated for me.

I had to run the following command to generate the file:

npx tsc --init

Now, you should be able to run your React project with TypeScript, if there are no TypeScript errors.

React Error: Attempted import error ‘X’ is not exported from

This error occurs when you try to import something that is not present in the specified file. When solving this error, check to make sure you are using the corresponding import and export mechanisms.

Default exports

Default exports are useful to export only a single object, function, variable. You can have only one default export per file.  

How to make a default export:

export default function Component() {
   return <h1>Hello, world</h1>
}

How to import a component with a default export:

import Component from './Component';

Notice that you do not use curly braces when importing a default export.

When importing a default export, you can use any name you want since there is only one in the file. For example:

import MyNewName from './Component';

Named import

You can have more than one named exports per file.

How to make a named export:

export function add(a, b) {
    return a + b;
}

export function subtract(a, b) {
    return a - b;
}

How to export multiple named modules at once:

export {
   add,
   subtract,
}

How to import a named export:

import { add, subtract } from './another-file';

When using named imports, the name of imported module must be the same as the name of the exported module.

You can also import both a default module and a named module on the same line, like so:

import Component, { add } from './another-file';

TypeScript Error: Generic type ‘Array’ requires 1 type argument(s).

This error comes from the use of Array without any type for the elements in the array. To solve this error, define a type for the contents of the array. You can define this in 2 different ways:

Solution 1:

clients: String[];

Solution 2:

clients: Array<String>;

If the type of the array can’t be determined, use any:

any[]
Array<any>

Note: Using the T[] syntax is recommended over Array<T> syntax

Testing Event Handlers in React Testing

In this short article, you will learn how to test event handlers, such as button clicks, with React Testing.

First, we will create a simple component to test:

import React from "react";

export default function ButtonWrapper({ title, ...props }) {
  return <button {...props}>{title}</button>;
}

This ButtonWrapper component takes in a title prop and any other props and returns a standard JSX button element.

Now, create a testing file with the same name as the component file and a .test.js extension (or .test.tsx if you are using TypeScript) (i.e. ButtonWrapper.test.js)

First, import the following from React testing and import the component:

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import ButtonWrapper from "./ButtonWrapper";

Now, create the test and give it a name (i.e. "handles onClick")

test("handles onClick", () => {
 
})

Render the ButtonWrapper component:

render(<ButtonWrapper title={"Add"} />);

We will add an onClick property to the button and call the jest.fn() function whenever the component is clicked:

const onClick = jest.fn();
render(<ButtonWrapper onClick={onClick} title={"Add"} />);

jest.fn() is a function created by Jest which tracks how often it is called. In other words, it will keep track of how many times the button component is clicked.

Now, we will get access to the button and click it using fireEvent.click():

const buttonElement = screen.getByText("Add");
fireEvent.click(buttonElement);

fireEvent.click() simulates a click on the button element.

Next, we will write an assertion for how many times the button has been clicked. First, we will write an inaccurate assertion:

expect(onClick).toHaveBeenCalledTimes(0);

Now, we will run our test:

yarn test

This test will not pass because we know that the button was clicked once by the fireEvent call. The output should look like this:

Basically, React Testing is saying that it expected 0 calls to be made, but it received 1 call.

Now, let’s make a correct assertion:

expect(onClick).toHaveBeenCalledTimes(1);

The output should look like this:

Here is the final code:

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import ButtonWrapper from "./ButtonWrapper";

test("handles onClick", () => {
  const onClick = jest.fn();
  render(<ButtonWrapper onClick={onClick} title={"Add"} />);
  const buttonElement = screen.getByText("Add");
  fireEvent.click(buttonElement);
  expect(onClick).toHaveBeenCalledTimes(1);
});

Now, you can test event handlers in React Testing. Thanks for reading!

Basic Component Testing with React Testing and TypeScript

In this article, we will create a simple React component and do some basic testing on the component using React Testing. This will help you get acquainted with the React Testing library and how to write tests.

Installation

The React Testing library comes by default when you run create-react-app, so it should already be in your project if you created your React project with that command.

Create TypeScript component

First, we will create a component using TypeScript. Make a file called Container.tsx

In the Container component, we will have a div element with a h1 inside of it:

import React from "react";

export const Container = ({ title }: { title: string }) => (
  <div role="contentinfo">
    <h1>{title}</h1>
  </div>
);

We define a title prop with a type of string in TypeScript with the code: { title }: { title: string }

We also set an aria-role for the div element of type contentinfo

Write Tests

Now, let’s write some tests for this component

Create a test file called Container.test.tsx

First, we need to add some imports:

import React from "react";
import { render, screen } from "@testing-library/react";
import { Container } from "./Container";

As you can see, we are importing render and screen from the React testing library, which we will use momentarily.

To create a new test, we use the following structure (which is based on Jest, which is the underlying test framework):

test("Name of test", () => {
   // Function body of test
});

So, we will create a test named “renders title” and then define it:

test("renders title, () => {
    // Test will go here
})

First, render the Container component:

render(<Container title={"New Container"} />);

Next, get access to an element:

const titleElement = screen.getByText(/New Container/i);

There are many ways to get access to an element in the library

  • getByText
  • getByRole (aria-role)
  • getByLabelText
  • getByPlaceholderText

Make an assertion about the element:

expect(titleElement).toBeInDocument()

The above assertion is simply that the title element is in the Container component. This is just a very simple test.

So, here is the full test code:

test("renders title, () => {
    render(<Container title={"New Container"} />);
    const titleElement = screen.getByText(/New Container/i);
    expect(titleElement).toBeInDocument()
})

Run yarn test

The test output should look like this if it passed successfully:

Now, you have a basic introduction to testing components in React.

How to Setup Redis in Node.js Project

Redis is an in-memory key-value database. You can think of it like one giant JSON object. It is unique from other databases in that it stores all data in working memory (RAM) instead of on your system’s hard disk. This allows Redis to be extremely fast at fetching data.

Although Redis can be used as a full database, it is typically used for caching, which is when data that is frequently accessed or takes a long time to compute is stored in working memory for easy access.

Setting up Redis in Node.js project

You can use the node-redis library to interact with Redis in your Node.js application. In this tutorial, you will learn how to establish a connection between Node.js and Redis.

The following steps:

  1. Create a new Node.js project
  2. Install Redis client in Node project
  3. Create a new Redis object in your server file
  4. Start the connection
  5. Run Redis & your Node project

Step 1: Create new Node.js project

Create a blank Node.js project with npm init

npm init -y

Step 2: Install Redis client

To use Redis with Node.js, you need to install a Node.js Redis client. Inside the project directory, install the node-redis client by running this command:

npm install redis

Step 3: Create a new Redis object

Once you’ve installed the redis module, you can access Redis in your Node application. The redis module’s createClient() method creates a Redis object:

const redis = require('redis')
const client = redis.createClient()

The Redis object will default to use 127.0.0.1 as the hostname and 6379 as the port.

To connect to a different host or port, use a connection string in the format redis[s]://[[username][:password]@][host][:port][/db-number] like so:

redis.createClient({
  url: 'redis://alice:foobared@awesome.redis.server:6380'
});

Step 4: Start the connection

Now, use the Redis object’s connect() function to start the connection with Redis on the default port 6379. Since the function returns a Promise, we must use async/await to handle it. We can use an anonymous self-invoking function which will run immediately after we define it:

(async () => {
  await client.connect();
});

You also call the Node.js on() method that registers events on the Redis object. For example, the following line checks for an error event and triggers a callback which logs the error:

(async () => {
  client.on("error", (err) => console.log("Redis Client Error", err));

  await client.connect();
});

Step 5: Run Redis & your Node project

Before you run your Node project, make sure you are running the Redis database in a seperate Terminal.

You can run the Redis database with the following command:

redis-server

You should see an output similar to this in your console when you run the command:

Now, you can run your Node project with node index.js. Although there won’t be any Terminal output (unless there is an error), you are now able to use Redis in your project.

How to Open VS Code settings.json file

This short tutorial will be on how to open the settings.json file in VS Code on Mac.

In the menu bar, navigate to Code > Preferences > Settings.

This will open the Settings UI page, which looks like this:

In the upper right-hand corner of the page, click the Open Settings (JSON) icon.

Now, you have opened the settings.json file!

How to Check if the Current Directory is a Git Repository

Use this command in your Terminal to determine if the directory you are currently in is a Git repository or not:

git rev-parse --is-inside-work-tree

The shell prints true if you are in a git repository’s working tree. It prints false if you are inside the ‘.git’ tree but not the working directory, and it prints a fatal error if it’s neither (not connected to any git repo).

Both ‘true’ and ‘false’ are printed on STDOUT with an exit status of 0, the fatal error is printed on STDERR with an exit status of 128.

LeetCode #125: Valid Palindrome (Solution in Python & Explanation)

In this article, I will be explaining how to solve the Valid Palindrome problem on LeetCode. This is an Easy-level problem.

The Problem

A phrase is a palindrome if, after converting all uppercase letters into lowercase letters and removing all non-alphanumeric characters, it reads the same forward and backward. Alphanumeric characters include letters and numbers.

Given a string s, return true if it is a palindrome, or false otherwise.

Example 1:

Input: s = "A man, a plan, a canal: Panama"
Output: true
Explanation: "amanaplanacanalpanama" is a palindrome.

Example 2:

Input: s = "race a car"
Output: false
Explanation: "raceacar" is not a palindrome.

Example 3:

Input: s = " "
Output: true
Explanation: s is an empty string "" after removing non-alphanumeric characters.
Since an empty string reads the same forward and backward, it is a palindrome.

Solution

Alphanumeric characters are the lowercase letters a-z, capital letters A-Z, and integers 0-9.

So, to solve this, we first want to remove all non-alphanumeric characters, including spaces, punctuation, etc., from the input string. Then, we need to check the new string to see if it’s a palindrome. We have two ways we can do this.

Solution #1

For this solution, we create a new string which contains all of the alphanumeric characters from the input string (lowercased). Then, we compare this new string to its reversed version. If they are the same, the function returns true. If not, it returns false.

In order to create the new string, we loop through the characters in the original input string and, if the character is alphanumeric, we append it to the new string.

Finally, we can check if the string (without any special characters or spaces) is a palindrome by using a boolean expression to equate it to its reverse. We can get the reverse of a string by using str[::-1]

def isPalindrome(self, s: str) -> bool:
    newStr = ""
    
    for char in s:
        if char.isalnum():
            newStr += c.lower()
    return newStr == newStr[::-1]

This solution has a time complexity of O(n) because we have to loop through the string to remove non-alphanumeric characters. It also has a space complexity of O(n) because we are creating another string, which could be at most the size of the input string.

Solution #2

For this solution, we will create pointers to the beginning and end of the array which will make sure that the two sides have the same characters in the same order.

First, create two pointers— one which points to the beginning of the string and the other which points to the end. The two pointers will move inwards, checking each character until the reach the middle or overlap with each other.

As the pointers loop through the characters, they will skip any characters that are not alphanumeric. To do this, we use while loops that will continue to run until the character at the left/right index is alphanumeric. We also add a check to make sure that the left/right index is in bounds by checking left < right / right > left

We must make sure to compare the lowercase versions of the characters to each other. If any character at the left pointer does not match the corresponding character at the right pointer, then we know that it’s not a palindrome and can return False. If the entire loop finishes then that must mean the string is a palindrome and we can return True.

def isPalindrome(self, s: str) -> bool:
    left, right = 0, len(s) - 1 
 
    while left < right:
        while left < right and not s[left].isalnum():
            left +=1
        while right > left and not s[right].isalnum():
            right -= 1
        if s[left].lower() != s[right].lower():
            return False 
        left, right = left + 1, right - 1
    return True

The time complexity for this solution is O(n) because you are still looping through the entire string. The space complexity is O(1) because you are not making a new string.

LeetCode #242: Valid Anagrams (Solution in Python & Explanation)

The Problem

Given two strings s and t, return true if t is an anagram of s, and false otherwise.

An Anagram is a word or phrase formed by rearranging the letters of a different word or phrase, typically using all the original letters exactly once.

Example 1:

Input: s = "anagram", t = "nagaram"
Output: true

Example 2:

Input: s = "rat", t = "car"
Output: false

Solution

We know that the two strings are anagrams if:

  1. They have the same length
  2. They have the same characters and the same quantity of each character

So, if we can verify that the strings pass these two criteria, we can verify that they are valid anagrams.

We can check if they are the same length by simply using len:

len(s) == len(t)   # boolean expression

But how can we check if they have the same characters and the same quantity of each character? We can use hash maps. For each string, we can create a hash map that includes every unique character in the string and its frequency in the string.

For example, this would be the hash map of the string s, which has a value of "anagram":

s = "anagram"
countS = {"a": 3, "n": 1, "g": 1, "m": 1}

Once, we have created the hash maps, we can check if they are equivalent by using a boolean expression:

countS == countT

So, let’s put it all together. These are all the steps we have to take:

  1. We check if the strings are not the same length. If they’re not, we can immediately return False without doing any other code.
  2. We create empty hash maps for the two strings s and t.
  3. We loop through each character in s and t and add them to the hash maps. If the character does not exist in the hash map, a new key for the character is added and the value is initialized to 0 (then we add 1). If a key for the character does exist in the hash map, we add 1 to the value.
  4. We check if the two hash maps are equivalent. If they are, we return True. If they are not, we return False.

This is the final code.

   def isAnagram(self, s: str, t: str) -> bool:
        # Step 1: check if same length
        if len(s) != len(t):
            return False
        
        # Step 2: create hash maps
        countS = {}
        countT = {}
        
        # Step 3: loop through characters to add to hash map
        for i in range(len(s)):
            countS[s[i]] = 1 + countS.get(s[i], 0)
            countT[t[i]] = 1 + countT.get(t[i], 0)
        
        # Step 4: check if hash maps are equivalent  
        if (countS == countT): 
            return True
        else: 
            return False

This solution has a time complexity of O(s + t) or simply O(n) because we have to iterate through each of the strings. The space complexity is also O(s + t) because we are building hash maps that are the sizes of arrays s and t.

There is another solution that is much more simple, but less efficient. We can sort both of the strings and see if the sorted versions are equivalent. This works because, if they are anagrams, then they will have the same sorted value.

def isAnagram(self, s: str, t: str) -> bool:
    return sorted(s) == sorted(t)

A good sorting algorithm can sort an array of characters in O(n * log(n)) time. The space complexity could be either O(1) or O(n)