Categories
JavaScript APIs

Getting Started with IndexedDB Manipulation with Dexie

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Getting started

We can get started by adding Dexie with a script tag:

<script src="https://unpkg.com/dexie@latest/dist/dexie.js"></script>

Then we can use it to create our data.

Then we can use it create a database, and write and read data from it.

To do this, we write:

const db = new Dexie("friend_database");
(async () => {
  try {
    await db.version(1).stores({
      friends: 'name,age'
    });
    await db.friends.put({
      name: "mary",
      age: 28
    })
    const friend = await db.friends.get('mary');
    console.log(friend.age);
  } catch (error) {
    console.log(error);
  }
})()

We create the database with the Dexie constructor.

Then we create a store in the database with:

await db.version(1).stores({
  friends: 'name,age'
});

We add the friends store into the database with the name and age fields indexed.

Indexed columns can be used to search for an entry.

Then we add the data with the put method:

await db.friends.put({
  name: "mary",
  age: 28
})

Then we get an entry with the given indexed column with:

const friend = await db.friends.get('mary');

Then get the value of the age field with:

console.log(friend.age);

Using Dexie as a Module

We can use Dexie withn the dexie module.

To install it, we run:

npm i dexie

Then can write the same code with:

import Dexie from "dexie";
const db = new Dexie("friend_database");

(async () => {
  try {
    await db.version(1).stores({
      friends: "name,age"
    });
    await db.friends.put({
      name: "mary",
      age: 28
    });
    const friend = await db.friends.get("mary");
    console.log(friend.age);
  } catch (error) {
    console.log(error);
  }
})();

The Dexie Class

The Dexie class is both a class and a namespace.

The Dexie instance represents a database connection.

It can also be used as an export area for functions, utilities, and classes.

If it’s used in the browser as a script tag, then only the window.Dexie property is added.

If it’s used as a module, then it’s available as a default export.

The Table Class

The table represents an object-store.

We have direct access to instances of Table for each object store that we’ve denied in our schema.

For example, if we have:

(async () => {
  const db = new Dexie("FriendsAndPetsDB");
  await db.version(1).stores({
    friends: "++id,name,isCloseFriend",
    pets: "++id,name,kind"
  });
  await db.open();
  await db.friends.add({
    name: "james",
    isCloseFriend: true
  });
  await db.pets.add({
    name: "mary",
    kind: "dog",
    fur: "long"
  });
})()

Then we created the friends and pets store.

db.friends and db.pets are the table instances.

And we can manipulate data with them.

db.open opens the database connection.

We called db.friends.add to add data to the db.friends store.

And we called db.pets.add to add data to the db.pets store.

Conclusion

We can manipulate IndexDB data easily with the Dexie library.

Categories
JavaScript APIs

Introducing the Intersection Observer API

We often have to watch for the visibility of an element within the window or a parent element.

To make this task easy, the Intersection Observer API is available for us to use to do that.

In this article, we’ll look at how to use the Intersection Observer API to watch for visibility changes for a child element.

Uses for the Intersection Observer API

We can use the Intersection Observer API to watch for visibility changes of one element or 2 elements in relation to each other.

Watching these changes is useful for things like:

  • lazy-loading images when an element becomes visible
  • infinite scrolling
  • checking the visibility of ads
  • run tasks only when an element is visible

Using the Intersection Observer API

To use the Intersection Observer API, we register a callback that runs whenever the visibility of an element changes. This means when it enters or leaves the viewport.

The callback is called whenever the element called the target intersection either the device viewport or a specified element.

The device viewport or another specified element is called the root element or root.

We have to specify the target element’s intersection ratio to indicate how much of the element is visible before the callback will be called.

It’s an array that has numbers ranges from 0 to 1, where 0 is not visible and 1 is completely visible.

For instance, given that we have the following elements generated by the following code:

for (let i = 0; i < 100; i++) {
  const p = document.createElement('p');
  p.innerText = i;
  document.body.append(p);
}

The code above creates p elements with a number as the text content.

We can create an IntersectionObserver object as follows:

const ps = document.querySelectorAll('p');
let options = {
  root: null,
  rootMargin: '0px',
  threshold: [0, 0.25, 0.5, 0.75, 1]
}

let callback = (entries, observer) => {
  entries.forEach(entry => {
    console.log(entry.intersectionRatio > 0)
  });
};

let observer = new IntersectionObserver(callback, options);
ps.forEach(p => {
  observer.observe(p);
});

In the code above, we first create our options object, which has root set to null so that we watch the elements relative to the root element.

We can also specify another element that we want to watch the child elements from.

The rootMargin property is the margin around each element that we want to watch. We can specify this value as we do with the value of the margin CSS property.

The threshold property is an array of numbers for which the visibility of each element is watched and the callback is called. In the example above, we specified that the callback is called when the element being watched is 0% visible, 25% visible, 50% visible, 75% visible, and 100% visible.

Next, we create the IntersectionObserver object with the IntersectionObserver constructor with the callback and options that we created earlier passed in.

Then we can observe each p element that was created earlier by writing:

ps.forEach(p => {
  observer.observe(p);
});

Where ps is:

const ps = document.querySelectorAll('p');

which are the p elements that we created earlier.

Then when we scroll up and down the page, we should see true or false logged from the callback when the intersectionRatio of the element is bigger than 0.

If intersectionRatio is bigger than 0 that means the element is at least partly visible. Otherwise, it’s not visible.

To make observing elements more efficient, we can unobserved them when they’re in view.

For instance, we can do that by adding observer.unobserve(entry.target); into our callback as follows:

let callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0) {
      observer.unobserve(entry.target);
    }
  });
};

We can also manipulate the elements in the callback when the intersectionRatio or any other property changes as follows:

let callback = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.intersectionRatio > 0.75) {
      entry.target.classList.add('highlight');
    } else {
      entry.target.classList.remove('highlight');
    }
  });
};

In the code above, we add the highlight class to the p element when the intersectionRatio is bigger than 0.75. This means the given element is more than 75% visible.

Given that the highlight class has the following CSS:

.highlight {
  color: green;
}

We get that the color of the text is green when the text is more than 75% visible on the page.

Conclusion

The Intersection Observer API makes it easy to see whether an element is visible or not. We can watch for changes in each element that we want to check the visibility of and then do things like add and remove classes or styles accordingly.

We can also unobserve elements that don’t need to be watched if we want to make our code more efficient.

Categories
JavaScript APIs

Using the React-Redux Hooks API to Manipulate Store State

With Redux, we can use it to store data in a central location in our JavaScript app. It can work alone and it’s also a popular state management solution for React apps when combined with React-Redux.

In this article, we’ll look at how to use the useDispatch and useStore hooks in the React-Redux hooks API, and also look at performance and stale state issues.

useDispatch

We can use the useDispatch hook to get the dispatch function to dispatch actions to the store.

For example, we use useDispatch as follows:

import React from "react";
import ReactDOM from "react-dom";
import { Provider, useSelector, useDispatch } from "react-redux";
import { createStore } from "redux";

function count(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(count);

function App() {
  const count = useSelector(state => state);
  const dispatch = useDispatch();
  return (
    <div className="App">
      <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      <p>{count}</p>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

In the code above, we called useDispatch in App to get the dispatch function so that we can dispatch actions from the store.

Then we create functions that call dispatch to pass into the onClick prop.

useStore

We can use the useStore hook to get the reference to the Redux store. It’s the same one that we passed into the Provider component.

This shouldn’t be used frequently since we aren’t supposed to access the store directly within a React app since it doesn’t update according to the React update cycle.

Stale Props and “Zombie Children”

Stale props mean that a selector function relies on a component’s props to extract data, a parent component would re-render and pass down new props as a result of an action, but the component’s selector functions execute before the component has a chance to re-render with those new props.

This causes outdated data to be displayed. in the child component or an error being thrown.

“Zombie child” refers to the cases where multiple nested connected components are mounted ina first pass, causing a child component to subscribe to the store before its parent. Then an action is dispatched that deletes data from the store. The parent component would stop rendering the child as a result.

Because the child subscribed to the store first, its subscription runs before the parent stops render. When it reads a value based on props, the data no longer exists. Then an error may be thrown when the extraction logic doesn’t check for the case of the missing data.

Therefore, we should rely on props in our selector function for extracting data.

connect adds the necessary subscription to the context provider and delays evaluating child subscriptions until the connected component has re-rendered. Putting a connected component in the component tree just above the component using useSelector will prevent the issues above.

Performance

useSelector will do a reference equality comparison of the selected value when running the selector function after an action is dispatched.

It’ll only re-render if the previous state is different from the current state. connect and useSelector doesn’t prevent the component from re-rendering because of its parent re-rendering.

To stop parent re-rendering from re-rendering the child, we can use memoization to prevent that.

For example, we can write the following:

import React from "react";
import ReactDOM from "react-dom";
import { Provider, useSelector, useDispatch } from "react-redux";
import { createStore } from "redux";

function count(state = 0, action) {
  switch (action.type) {
    case "INCREMENT":
      return state + 1;
    case "DECREMENT":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(count);

function App() {
  const count = useSelector(state => state);
  const dispatch = useDispatch();
  return (
    <div className="App">
      <button onClick={() => dispatch({ type: "INCREMENT" })}>Increment</button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>Decrement</button>
      <p>{count}</p>
    </div>
  );
}

App = React.memo(App);

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);

In the code above, we added the React.memo call as follows:

App = React.memo(App);

to cache existing results and preventing re-render if the state remains the same.

Conclusion

We can use the useDispatch hook to get the dispatch function to update the store’s state.

There’s also a useStore hook to get a reference of the Redux store in our React components.

However, we shouldn’t access the store directly with useStore since we’re supposed to use useSelector to get the latest state from the store and use useDispatch to dispatch actions.

Also, we have to be aware of stale props from parent components and child components that have been removed because of state changes throwing errors and displaying incorrect data.

We should also memoize the state of components to cache them so that they don’t have to re-render when nothing changed.

Categories
JavaScript APIs

Introducing the JavaScriptWeb Animations API

The JavaScript Web Animations API is a new API that lets us create animations with JavaScript by manipulating the elements that we want to animate.

In this article, we’ll look at how to create simple animations with the API.

Basic Usage

The way we create the animation is similar to how we do it with CSS. We specify how the element animates by specifying how we transform the element in keyframes. The only difference is that we do it with JavaScript instead of CSS.

To animate an element, we call the animate method on the element with 2 arguments. The first argument is an array with the keyframe for each entry. Each keyframe object has the styles to apply when the keyframe is displayed.

The second object is the timing object, which has the duration and iterations properties. duration is the number of milliseconds to animate the element. The iteration is the number of iterations to run the animation for. It can be a positive number of Infinity .

For instance, we can write the following HTML and JavaScript to animate an object:

HTML:

<img src='https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg' width=100 height=100>

JavaScript:

const action = [{
    transform: 'rotate(0) translate3D(-50%, -50%, 0)',
    color: '#000'
  },
  {
    color: '#431236',
    offset: 0.3
  },
  {
    transform: 'rotate(360deg) translate3D(-50%, -50%, 0)',
    color: '#000'
  }
];

const timing = {
  duration: 3000,
  iterations: Infinity
}

document.querySelector("img").animate(
  action,
  timing
)

The action array has the keyframe styles and the timing object has the duration and the number of iterations of the animation to run.

Then we should see an image that rotates forever.

Controlling playback with play(), pause(), reverse(), and playbackRate

The animate method returns an object with the play , pause and reverse methods. playbackRate is a numerical property that can be set by us. A negative playback rate means the animation plays in reverse.

For instance, we can add buttons to call these methods as follows. First, we add the following HTML code to add buttons for the playing, pausing, and reversing, and also a range slider for changing the playback rate:

<button id='play'>Play</button>
<button id='pause'>Pause</button>
<button id='reverse'>Reverse</button>
<input type='range' min='-2' max='2'>

<img src='https://interactive-examples.mdn.mozilla.net/media/examples/grapefruit-slice-332-332.jpg' width=100 height=100>

Then we add some CSS to move the image:

img {
  position: relative;
  top: 100px;
  left: 100px;
}

Finally, we add the JavaScript for the animation and event handler for the buttons and input that call the methods and set the playbackRate property as follows:

const action = [{
    transform: 'rotate(0) translate3D(-50%, -50%, 0)',
    color: '#000'
  },
  {
    color: '#431236',
    offset: 0.3
  },
  {
    transform: 'rotate(360deg) translate3D(-50%, -50%, 0)',
    color: '#000'
  }
];

const timing = {
  duration: 3000,
  iterations: Infinity
}

const animation = document.querySelector("img").animate(
  action,
  timing
)

const play = document.getElementById('play');
const pause = document.getElementById('pause');
const reverse = document.getElementById('reverse');
const range = document.querySelector('input');

play.onclick = () => animation.play();
pause.onclick = () => animation.pause();
reverse.onclick = () => animation.reverse();
range.onchange = () => {
  const val = range.value;
  animation.playbackRate = +val;
}

In the code above, we added the buttons. Then in the JavaScript, we have:

const play = document.getElementById('play');
const pause = document.getElementById('pause');
const reverse = document.getElementById('reverse');
const range = document.querySelector('input');

play.onclick = () => animation.play();
pause.onclick = () => animation.pause();
reverse.onclick = () => animation.reverse();
range.onchange = () => {
  const val = range.value;
  animation.playbackRate = +val;
}

to call the methods and set the properties on animation , which is assigned to the animation object that’s returned by animate .

Then we can do whatever action is indicated by the method’s names and also set the playback rate when we move the slider.

Getting Information Out of Animations

We can also get more information out of the animation object, like the duration and the current time of the animation.

We can get all that information from the animation object in the code above. In Chrome, we have the startTime and currentTime to get the start time and current time of the animation respectively.

It also has the playState and playbackRate . The properties of the animation object may differ between browsers.

Conclusion

We can create simple animations with the Web Animations API. It’s a simple API that lets us define keyframes for animation as we do with CSS animations.

The only difference is that we can do more things like changing the playback rate, and controlling the playback of the animation with play, pause, and reverse.

Categories
JavaScript APIs

Side Projects That We Can Create With Free APIs

In the software development world, practice makes perfect. Therefore, we should find as many ways to practice programming as possible. With free public APIs, we can practice programming by creating apps that use those APIs.

In this article, we’ll look at some practice project ideas that can use some of those APIs.

Email Address Validator

We can use the MailboxValidator API to validate the email address so that we can make sure that are sending our email to a valid email address.

It’s great for marketers and salespeople to clean up their email lists so that they aren’t sending stuff to defunct or invalid email addresses.

The API can be accessed by signing up for an API key.

Email Automation App

We can write our own app to send emails by using the Mailgun API. It lets us send emails in batches to the email address of our choice.

Just make sure that we use it responsibly so that we aren’t sending our spam.

It has SDKs for many platforms like Python, Ruby, Perl, Java, Kotlin, Go, C#, Go, Node.js, and Luvit.

It can also be called from cURL directly.

It has a free tier so we can use it for practice and if we like it and want to use it for more things, we can pay for more access to the API.

The free tier according to this webpage has the following:

  • send 5000 messages a month
  • send 300 messages a day from the sandbox domain
  • data retention for logs for a day
  • no custom domains
  • send up to 5 authorized recipients max

That’s definitely good enough if we’re only creating a practice app with it.

It also has APIs for email validation and API for sending more than a million emails a day.

Also, it has a deliverable service to improve deliverability.

Google Calendar App

With the Google Calendar API, we can create our programs to customize bring Google Calendar functionality to our own app.

We can use it to display, create and modify Google Calendar events.

Also, we can get push notifications, batch requests, and create and get reminders and notifications.

File Sharing App

We can use the Box, Dropbox, Google Drive, or OneDrive APIs to upload and download files from those provides automatically.

They all use OAuth authentication, so we can practice using that. With these APIs, we can automate the upload and download of data the way we please.

Also, we can automatically share data with other people of our choice.

With the Pastebin API, we can upload text to Pastebin and share it with other people. All it takes is registering for a free API key then we’re set.

Cryptocurrency Apps

There’re lots of cryptocurrency APIs to get various kinds of cryptocurrency information like exchange rates and prices of various cryptocurrencies.

Also, there are many APIs that are used for trading cryptocurrencies automatically.

This can be our opportunity to beat the market by using programs to help us trade faster than other people that don’t know how to program.

For instance, to get cryptocurrency information, there are the Coinbase, CoinAPI, and CoinDesk APIs. They all require an API key to get access to them.

If we want to write programs to trade cryptocurrency, we can use the Bitfinex, Bitmex, and Bittrex APIs. They all require an API key to get access to them.

Real Currency Apps

There are also many APIs for getting real currency data.

We can get exchange rates from the Exchangeratesapi.io, ExchangeRate-API, Czech National Bank, Fixer.io and Frankfurter APIs.

Most of them don’t need any authentication to get access to their data except the Frankfurter API, which requires an API key.

Photo by Pepi Stojanovski on Unsplash

Data Validation Apps

We can use data validation APIs to validate various kinds of data.

For instance, we can send email addresses, phone numbers, VAT numbers, and domain names to the Cloudmersive Validate API to validate them.

All we need is an API key to gain access to the API.

To check the language that a given piece of text is written in, we can use the languagelayer API to check what language it’s written.

It requires no authentication for access.

Conclusion

We can write practice apps to validate various kinds of data like email address and the language that something is written in with various data validation APIs.

Also, we can use free APIs to automate tasks like sending emails and updating our calendars.

Of course, the free version of those APIs aren’t going to very useful compared to the paid counterparts, but it’s still plenty good for using them for practice apps.