React API Tutorials

Using the Redux API (Redux API Example)

What is Redux?

Redux is a state management framework for the front-end.

At its core, the Redux API is very simple:

  1. You implement a function that:
    1. Takes the previous state
    2. Takes an update action
    3. Returns new state
  2. You provide it the initial state
  3. You subscribe to updates:
    1. In response, can update the view
    2. Or you can make API calls

If you’re familiar with function programming, this concept should be familiar: Redux is very similar to a reducer function. But instead of reducing an array of values into a single value, Redux gives you an API that reduces a value and an update into a new value.

Why use Redux?

Most real-world apps are made up of a root app component that contains multiple layers of self-contained components. These sub-components often have their own local state, isolated and encapsulated from the rest of the app.

But when app data needs to span multiple levels, the root app component needs to orchestrate this data management. This can become unwieldy and overcomplicated quickly, unless proper care is taken.

This is where Redux comes in. As apps grow in size and complexity, state management needs to become disciplined. Redux was one of the first and most popular solutions to this problem. It provides a clean implementation of one-way data binding.

Redux centralizes three aspects of data management:

  • Data storage: Redux stores all data in a centralized location in memory, called a Redux Store. The rest of your app interacts with your app state through this Redux store object.
  • Update logic: All logic for updating data based on actions is located in the root reducer function. This function may be made by composing smaller update functions, which each manage portions of the data.
  • View updates: Your app subscribes to updates to changes in the Redux store’s data, and updates your views when necessary.

When should we use Redux?

The general rule of good software development is to use the simplest solution until it’s no longer enough for the problem at hand. This is also true of data management. So a decision flowchart for Redux could look like this:

  1. Will JavaScript POJOs and functions work? If your app is simple enough, you might be able to store your data in JavaScript objects and arrays, using functions such as map and reduce to update them, and call functions manually when they update.
  2. Can you design your own ad hoc event system? JavaScript’s “addEventListener” is a great model to imitate: design a set of events, emit them at the right time, and listen for them from the right objects. This is in fact how VS Code manages its own complex data needs.
  3. If your data and updating logic would benefit from centralization, Redux might just be the right tool for the job. Unlike an ad hoc event system, Redux centralizes all data flow and update logic. This helps makes it easy to catch bugs during early development.

Another very powerful aspect of Redux is the developer tools suite that the Redux community has made for it, which includes the ability to inspect and manipulate the Redux store in real-time. We’ll see in just a bit how beneficial this can be.

Using Redux in a React app

Redux is a general-purpose state management library, not tied to any particular view framework (such as React.js or Vue.js). That said, it is most popularly used with such frameworks, which are growing in popularity.

For a guided tutorial on how to integrate React with Redux, check out How to Use Redux with React. The Redux React API has two main flavors:

  1. The original API was the connect function, which uses Higher-Order Components (HOCs).
  2. Alternatively, Redux now integrates with React’s new Hooks API, using the Redux Hooks API.

Should I use the new React Context API or Redux in my React app?

React’s Context API by itself is not a full replacement for Redux. The Context API only deals with letting parent components share data and functions with child components deep in the component tree, without having to pass these through the tree as props. However, there is a new function in React’s Hooks API called useReducer, which works very similar to Redux: it allows you to write a function that takes old state and an update action, and returns new state. This new useReducer hook can replace the state-updating logic of Redux, and React’s Context API can replace the data centralization that Redux provides.

In this article, we’ll focus on using the Redux API by itself, in order to keep things simple.

Using the Redux API

The basics of Redux’s API

Using Redux is simple:

  1. Create a reducer function with this signature: (state, action) => state
  2. Create a Redux store with: Redux.createStore(reducerFunction)
  3. Subscribe to updates with: store.subscribe(updateViewFunction)
  4. Dispatch new actions with: store.dispatch(actionObject)

Typically the actionObject makes use of a common key, such as “type”, which the reducer function can switch on, in order to know how to transform the new state to the old state. The actionObject often has other keys, which store data needed to transform the state.

For example, if incrementing a counter, the "type" key might be "increment", and there might be a "by" key specifying a number to increment the counter by. This is a simple example, but the same concept applies no matter how complex your app’s state management needs are.

Simple real-world Redux example

Redux can either be used in an NPM project, such as with create-react-app, or using a simple <script> tag in a regular HTML file.

Create a new file called index.html and paste the following into it:

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

<title>Redux Example</title>
<p>Counter: <span id="value">0</span></p>
<button id="increment">Plus 1</button>
<button id="decrement">Minus 1</button>

<script>
  function updateCounter(state, action) {
    switch (action.type) {
      case 'INCREMENT': return state + 1;
      case 'DECREMENT': return state - 1;
      default: return state;
    }
  }

  const store = Redux.createStore(
    updateCounter,
    0,
  );

  const valueEl = document.getElementById('value');
  function updateView() {
    valueEl.innerHTML = store.getState().toString();
  }

  updateView();
  store.subscribe(updateView);

  document.getElementById('increment').addEventListener('click', () => {
    store.dispatch({ type: 'INCREMENT' });
  });

  document.getElementById('decrement').addEventListener('click', () => {
    store.dispatch({ type: 'DECREMENT' });
  });
</script>

Thanks to the semantics of HTML5, we don’t need anything more than this to get a working JavaScript app up and running.

Try it out in your browser and you’ll see something like this:

Understanding our Redux app, line by line

First we imported Redux using a simple <script> tag:

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

Then we set up our view. For now, we’re using simple HTML elements and DOM manipulation. So we created the following HTML to work with:

<p>Counter: <span id="value">0</span></p>
<button id="increment">Plus 1</button>
<button id="decrement">Minus 1</button>

The next thing we created was our reducer function:

function updateCounter(state, action) {
  switch (action.type) {
    case 'INCREMENT': return state + 1;
    case 'DECREMENT': return state - 1;
    default: return state;
  }
}

This reducer function should be self-explanatory: the action key has a “type” which can either be “INCREMENT” or “DECREMENT”, and our state is a number, so we return a new number. We also have a default case which just returns the state as-is. It’s good practice to leave this in each reducer to avoid errors.

Next, we used this reducer function to create our Redux store:

const store = Redux.createStore(
  updateCounter,
  0,
);

This function takes either one or two arguments. The first is always the reducer function, and the second is the initial state. If you omit the initial state, it defaults to “undefined”, per typical JavaScript semantics.

Now we need to subscribe to updates, so that something useful will happen when we dispatch a Redux action. We already have a <span> tag that we can use to store the counter, we simply created a function that updates our view:

const valueEl = document.getElementById('value');
function updateView() {
  valueEl.innerHTML = store.getState().toString();
}

This view-updating code is straightforward: Since the state is a number representing the count, turn it into a string, and set it as the contents of the span.

Then we subscribe to it immediately. And since the span starts off empty, but our state starts off with a valid initial value (0), we also call our render function right away:

updateView();
store.subscribe(updateView);

The last thing is to hook up the buttons to actually dispatch actions:

document.getElementById('increment').addEventListener('click', () => {
  store.dispatch({ type: 'INCREMENT' });
});

document.getElementById('decrement').addEventListener('click', () => {
  store.dispatch({ type: 'DECREMENT' });
});

This adds simple “click” event handlers. The only special thing here is that we call store.dispatch() and give it our action object, with a “type” key that our reducer knows how to handle.

And that’s it! That’s all we needed for a working Redux counter app.

The advantage of Redux developer tools

One of the great benefits of Redux is the advanced developer tools available for it, such as the Redux DevTools Extension.

You can install this through the Chrome Web Store.

After installing it, change the store to pass a third argument:

const store = Redux.createStore(
  updateCounter,
  0,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

The ReDux DevTools also requires that the Redux app is running in a server, even if only localhost, rather than from a file, so we’ll need to run a local web server. Here are the full instructions for viewing our Redux example app in Redux DevTools:

  1. Open a development console or terminal in the directory where you put “index.html”
  2. Run python -m SimpleHTTPServer
  3. Open localhost:8080 in Google Chrome
  4. Open Chrome’s DevTools Console
  5. Select the “Redux” tab in Chrome’s DevTools
  6. Select “Redux Example” in the right-tab inside the Redux DevTools panel
  7. Try out your app, and notice what happens in the new Redux panel

It should look something like this:

Using Redux with APIs

Because real-world apps need to call third-party APIs over the web, such as one of the many types of APIs available on RapidAPI.com, one of the first things a good developer looks for in a new framework or library is whether it has an async API, using callbacks or Promises. This avoids spinning cursors and unresponsive UIs that don’t respond to scrolls or clicks.

One benefit of Redux is that there is nothing special needed to call it async in the typical case. Because the updater function just takes the current state, an action, and returns the new state, it can be called sometime in the future, such as with setTimeout or setInterval, and it will update the app based on what its data looks like when it runs, rather than when it’s called.

In other words, we can simply call store.dispatch(...) from within a callback, or after awaiting a Promise in an async function.

For more complex needs, the Redux website has advice, best practices, and additional libraries that help with Async Actions and Async Flow.

Note: This tutorial assumes the reader has a firm understanding of how to use an API and how to make API requests.

Simple Redux API Tutorial

Figuring out how the API works

Now that we have a pretty solid understanding of how Redux works, let’s use it to make something more interesting: a time-traveling Currency Exchange app.

One of the APIs on RapidAPI.com that caught my attention was Currency Converter because it has a “Historical Currency Data” endpoint.

Connect to the Currency Converter API

This takes a date, two currencies to convert from/to, and a dollar amount to convert. To try out this API, we’ll use January 1, 2020 as our date, $1 as our amount, and we’ll convert from EUR to USD.

Need a refresher on API Endpoints? Check out this article: What is an Endpoint? Before continuing this tutorial, make sure to sign up for a RapidAPI.com account in order to try out the free Currency Converter API. If you’re feeling stuck at this point, you can follow along with the RapidAPI Quick Start Guide.

First, test out the API’s endpoint on its web page, and you’ll see something like this:

Great. This is enough to get us started on integrating this API with Redux.

Working out the JavaScript API call

On the Code Snippets section, you’ll see a JavaScript section with a fetch sample:

fetch("https://currency-converter5.p.rapidapi.com/currency/historical/2020-01-01?from=EUR&amount=1&format=json&to=USD", {
  "method": "GET",
  "headers": {
    "x-rapidapi-host": "currency-converter5.p.rapidapi.com",
    "x-rapidapi-key": /* Paste your RapidAPI key here! */  }
})
.then(response => {
  console.log(response);
})
.catch(err => {
  console.log(err);
});

Full Redux Currency Exchange example

We’re going to use this CurrencyExchange API to make a very simple visualization of the conversion rate between the two currencies, Euros and US Dollars, on Jan 1 of 2010-2020. At the end, it should look like this:

Here’s the full working sample of the code that we’ll walk through.

Paste the following into a new HTML file:

<!DOCTYPE html>
<html>

<head>
  <title>Redux Example</title>
  <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script>

  <style>
    .currency {
      width: 100px;
      background: #f0f0f0;
      box-sizing: border-box;
      padding: 5px;
      margin-bottom: 5px;
      background: #eee;
      border: 1px solid #aaa;
    }
  </style>
</head>

<body>
  <p>Day: <span id="day">0</span></p>
  <div class="currency" id="eur">EUR</div>
  <div class="currency" id="usd">USD</div>

  <script>
    function sleep(ms) {
      return new Promise(resolve => {
        setTimeout(resolve, ms);
      });
    }

    function updateRate(state, action) {
      switch (action.type) {
        case 'RATE': {
          return {
            rate: action.rate,
            date: action.date,
          };
        }
        default: return state;
      }
    }

    async function fetchRate({ date, from, to, amount }) {
      const response = await fetch(`https://currency-converter5.p.rapidapi.com/currency/historical/${date}?from=${from}&amount=${amount}&format=json&to=${to}`, {
        "method": "GET",
        "headers": {
          "x-rapidapi-host": "currency-converter5.p.rapidapi.com",
          "x-rapidapi-key": /* Paste your RapidAPI key here! */,
        }
      });
      // console.log(JSON.stringify(await response.text()));
      return await response.json();
    }

    const store = Redux.createStore(
      updateRate,
      { date: 'n/a', rate: 1 },
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    );

    function updateView() {
      const { date, rate } = store.getState();
      document.getElementById('day').innerHTML = date.toString();
      document.getElementById('usd').style.width = (rate * 100) + 'px';
    }

    updateView();
    store.subscribe(updateView);

    (async function () {
      for (let yyyy = 2010; yyyy <= 2020; yyyy++) {
        const date = `${yyyy}-01-01`;
        const json = await fetchRate({
          date,
          from: 'EUR',
          to: 'USD',
          amount: '1',
        });
        console.log(json);
        store.dispatch({
          type: 'RATE',
          rate: parseFloat(json.rates.USD.rate),
          date,
        });
        await sleep(2000);
      }
    })();
  </script>
</body>

</html>

NOTE: Your RapidAPI key will be on the Currency Exchange page. Make sure to paste it into the JavaScript code in your HTML file in the right spot, or you’ll get runtime errors.

Going through our example, one piece at a time

You might notice that we made our fetch function nicer, using async functions and template strings:

async function fetchRate({ date, from, to, amount }) {
  const response = await fetch(`https://currency-converter5.p.rapidapi.com/currency/historical/${date}?from=${from}&amount=${amount}&format=json&to=${to}`, {
    "method": "GET",
    "headers": {
      "x-rapidapi-host": "currency-converter5.p.rapidapi.com",
      "x-rapidapi-key": /* Paste your RapidAPI key here! */,
    }
  });
  return await response.json();
}

Near the end of this file, we invoke an anonymous async function, in which:

  1. We use a for-loop to run through each year from 2010 to 2020.
  2. We fetch the currency exchange rate on January 1st of that year.
  3. When we get the rate, we dispatch a new “RATE” event.
  4. Finally, we sleep for two seconds to avoid spamming the API.
(async function () {
  for (let yyyy = 2010; yyyy <= 2020; yyyy++) {
    const date = `${yyyy}-01-01`;

    const json = await fetchRate({
      date,
      from: 'EUR',
      to: 'USD',
      amount: '1',
    });

    console.log(json);

    store.dispatch({
      type: 'RATE',
      rate: parseFloat(json.rates.USD.rate),
      date,
    });

    await sleep(2000);
  }
})();

Connect to the Currency Converter API
The reason we’re wrapping this in an async function and invoking it right away, is so that we have the convenience of using await syntax with our fetchRate function and with the new sleep function, all within a simple JavaScript for-loop.

This sleep function is simple, we just wait for the specified time using setTimeout, and then resolve our promise:

function sleep(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

Our Redux store looks almost the same as in our first example app, except we give it our new updateRate function, and a new initial state that holds the date and the rate for that date:

const store = Redux.createStore(
  updateRate,
  { date: 'n/a', rate: 1 },
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

Because the focus of our Redux app is on time-traveling in the Redux DevTools, our updateRate function just stores the action’s details in the Redux state:

function updateRate(state, action) {
  switch (action.type) {
    case 'RATE': {
      return {
        rate: action.rate,
        date: action.date,
      };
    }
    default: return state;
  }
}

All that’s left is to create a view in HTML:

<style>
  .currency {
    width: 100px;
    background: #f0f0f0;
    box-sizing: border-box;
    padding: 5px;
    margin-bottom: 5px;
    background: #eee;
    border: 1px solid #aaa;
  }
</style>

<p>Day: <span id="day">0</span></p>
<div class="currency" id="eur">EUR</div>
<div class="currency" id="usd">USD</div>

And to write JavaScript that updates it, which we invoke immediately, and subscribe to changes in the Redux store:

function updateView() {
  const { date, rate } = store.getState();
  document.getElementById('day').innerHTML = date.toString();
  document.getElementById('usd').style.width = (rate * 100) + 'px';
}

updateView();
store.subscribe(updateView);

We’re updating the day in a <span>, and setting the width of the USD <div> to be (100 * rate) pixels.

Testing it out

After you run this Redux app, you can play around with the Redux DevTools to “travel” back through time, to compare currency rates over the past 10 years.

 

Connect to the Currency Converter API

How did Redux help us?

In our Redux Currency Exchange sample app, we have simple logic:

  1. Fetch data every 2 seconds from an external API
  2. When we get the data, use it to update the Redux store
  3. When the Redux store changes, update the view

But we see that this follows the same pattern as any real-world app:

  1. Setup initial app state and data
  2. Setup user-interaction event handlers
  3. Setup time-based event handlers (polls, timers, etc)
  4. Update app state based on these events
  5. Update the view whenever app state changes

Redux can help make this process simpler and more maintainable, by:

  • Centralizing all data, and the logic to update it, into a single location in our source code
  • Making it easier to inspect, manipulate, and debug data from Redux DevTools
  • Encouraging returning immutable data from the reducer function, rather than mutating it
  • Avoiding spaghetti logic by funneling all application updates through a central store

FAQ

Does Redux require using immutable data?

Redux works best when used with immutable data. This means structured data, such as arrays or JavaScript objects, where the keys and values don't change. Using immutable data structures helps Redux by guaranteeing certain properties about your data which it can then optimize around, and build tools around, such as the Redux Devtools that we encountered earlier in this article. It also makes debugging easier, since mutations are harder to trace.

How is the 'connect' function related to Redux?

By itself, Redux doesn't need anything more than what we've already covered in this article. We have already seen a full working Redux app. But when integrating with React apps, a bit more work is needed. The older integration involved creating Higher-Order Components (HOCs) using the 'connect' function. Alternatively, since the React Hooks API came out, Redux now has a React Redux Hooks API that can often be simpler and easier to use.

If Redux uses global variables, isn't that an anti-pattern?

In traditional apps, global variables indicated an anti-pattern where data was being passed around the application in a disorganized way, leading to code that's difficult to decouple or maintain. But in modern Single Page Apps (SPAs) for the web, the whole web page's state often represents a single document, which needs to access the same state, or take action on the same conceptual data, but from different parts of the UI. Because Redux keeps its logic centralized by having a single processing point for all application business logic, it avoids the clutter associated with globals in traditional apps, while offering a convenient way for all components within the app to affect the whole app.

5/5 - (1 vote)
Share
Published by

Recent Posts

Power Up Your Enterprise Hub: New March Release Boosts Admin Capabilities and Streamlines Integrations

We're thrilled to announce the latest update to the Rapid Enterprise API Hub (version 2024.3)!…

2 weeks ago

Unveiling User Intent: How Search Term Insights Can Empower Your Enterprise API Hub

Are you curious about what your API consumers are searching for? Is your Hub effectively…

2 weeks ago

Rapid Enterprise API Hub Levels Up Custom Branding, Monetization, and Management in February Release

The RapidAPI team is excited to announce the February 2024 update (version 2024.2) for the…

4 weeks ago

Supercharge Your Enterprise Hub with January’s Release: Search Insights, Login Flexibility, and More!

This January's release brings exciting features and improvements designed to empower you and your developers.…

3 months ago

Enhanced Functionality and Improved User Experience with the Rapid API Enterprise Hub November 2023 Release

Rapid API is committed to providing its users with the best possible experience, and the…

5 months ago

The Power of Supporting Multiple API Gateways in an API Marketplace Platform

In today's fast-paced digital world, APIs (Application Programming Interfaces) have become the backbone of modern…

6 months ago