Search by Algolia
Vector vs Keyword Search: Why You Should Care
ai

Vector vs Keyword Search: Why You Should Care

Search has been around for a while, to the point that it is now considered a standard requirement in many ...

Nicolas Fiorini

Senior Machine Learning Engineer

What is a B2B marketplace?
e-commerce

What is a B2B marketplace?

It’s no secret that B2B (business-to-business) transactions have largely migrated online. According to Gartner, by 2025, 80 ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

3 strategies for B2B ecommerce growth: key takeaways from B2B Online - Chicago
e-commerce

3 strategies for B2B ecommerce growth: key takeaways from B2B Online - Chicago

Twice a year, B2B Online brings together industry leaders to discuss the trends affecting the B2B ecommerce industry. At the ...

Elena Moravec

Director of Product Marketing & Strategy

Deconstructing smart digital merchandising
e-commerce

Deconstructing smart digital merchandising

This is Part 2 of a series that dives into the transformational journey made by digital merchandising to drive positive ...

Benoit Reulier
Reshma Iyer

Benoit Reulier &

Reshma Iyer

The death of traditional shopping: How AI-powered conversational commerce changes everything
ai

The death of traditional shopping: How AI-powered conversational commerce changes everything

Get ready for the ride: online shopping is about to be completely upended by AI. Over the past few years ...

Aayush Iyer

Director, User Experience & UI Platform

What is B2C ecommerce? Models, examples, and definitions
e-commerce

What is B2C ecommerce? Models, examples, and definitions

Remember life before online shopping? When you had to actually leave the house for a brick-and-mortar store to ...

Catherine Dee

Search and Discovery writer

What are marketplace platforms and software? Why are they important?
e-commerce

What are marketplace platforms and software? Why are they important?

If you imagine pushing a virtual shopping cart down the aisles of an online store, or browsing items in an ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

What is an online marketplace?
e-commerce

What is an online marketplace?

Remember the world before the convenience of online commerce? Before the pandemic, before the proliferation of ecommerce sites, when the ...

Catherine Dee

Search and Discovery writer

10 ways AI is transforming ecommerce
e-commerce

10 ways AI is transforming ecommerce

Artificial intelligence (AI) is no longer just the stuff of scary futuristic movies; it’s recently burst into the headlines ...

Catherine Dee

Search and Discovery writer

AI as a Service (AIaaS) in the era of "buy not build"
ai

AI as a Service (AIaaS) in the era of "buy not build"

Imagine you are the CTO of a company that has just undergone a massive decade long digital transformation. You’ve ...

Sean Mullaney

CTO @Algolia

By the numbers: the ROI of keyword and AI site search for digital commerce
product

By the numbers: the ROI of keyword and AI site search for digital commerce

Did you know that the tiny search bar at the top of many ecommerce sites can offer an outsized return ...

Jon Silvers

Director, Digital Marketing

Using pre-trained AI algorithms to solve the cold start problem
ai

Using pre-trained AI algorithms to solve the cold start problem

Artificial intelligence (AI) has quickly moved from hot topic to everyday life. Now, ecommerce businesses are beginning to clearly see ...

Etienne Martin

VP of Product

Introducing Algolia NeuralSearch
product

Introducing Algolia NeuralSearch

We couldn’t be more excited to announce the availability of our breakthrough product, Algolia NeuralSearch. The world has stepped ...

Bernadette Nixon

Chief Executive Officer and Board Member at Algolia

AI is eating ecommerce
ai

AI is eating ecommerce

The ecommerce industry has experienced steady and reliable growth over the last 20 years (albeit interrupted briefly by a global ...

Sean Mullaney

CTO @Algolia

Semantic textual similarity: a game changer for search results and recommendations
product

Semantic textual similarity: a game changer for search results and recommendations

As an ecommerce professional, you know the importance of providing a five-star search experience on your site or in ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

What is hashing and how does it improve website and app search?
ai

What is hashing and how does it improve website and app search?

Hashing.   Yep, you read that right.   Not hashtags. Not golden, crisp-on-the-outside, melty-on-the-inside hash browns ...

Catherine Dee

Search and Discovery writer

Conference Recap: ECIR23 Take-aways
engineering

Conference Recap: ECIR23 Take-aways

We’re just back from ECIR23, the leading European conference around Information Retrieval systems, which ran its 45th edition in ...

Paul-Louis Nech

Senior ML Engineer

What is a neural network and how many types are there?
ai

What is a neural network and how many types are there?

Your grandfather wears those comfy slipper-y shoes all day, every day, and they’re starting to get holes in ...

Vincent Caruana

Sr. SEO Web Digital Marketing Manager

Looking for something?

facebookfacebooklinkedinlinkedintwittertwittermailmail

At Algolia, we do a lot of front-end JavaScript, because for us UX and UI are key components of a great search – we want the search experience to be perfect across the entire stack.

Recently, we’ve been playing quite a lot with React in our projects, especially in Instantsearch.js, an open source library for building complex search UI widgets. So far our experience has been very positive; it was so good that we eventually decided to introduce React onto our dashboard, along with Redux to handle shared state across components.

We’re really happy with the benefits that both have brought to our stack; in particular, we found that Redux brought quite a few cool things to the table:

  • Testability: Because view components essentially hold no logic, it is very easy to test them – just provide them with data and spy functions, and check that it renders the way you want;
  • Simplicity: We noticed that using Redux led us to write very clean and elegant code: no obscure features or leaky abstractions means that we always know what’s going on and virtually never run into cryptic, framework specific errors like some of AngularJS’s errors;
  • Developer experience: There are great developers tools (i.e. check out Redux Devtools, it’s awesome!) out there which allows you to easily inspect Actions and State mutations; on top of that you can also rely on very cool features such as hot reload and, even better, time travel: fixing bugs it’s just a matter of rewinding / forwarding a set of actions, find the state change which caused the error, fix it and finally replay the actions.

If you are not familiar with React or Redux as yet, there are many great resources available online for both.

React and Redux give you great powers, but also great responsibilities ! You are free to handle many things exactly the way you want. However, to really harness their combined potential, we’ve found that setting up and enforcing conventions is crucial.

Organising your single page application

We’re using the following fractal directory structure. Each page is an almost self contained app, asynchronously loading its own subpages.

- routes
 - page1
   - actions
   - components
   - containers
   - routes
     - subpage1
       ...

There’s nothing particularly controversial here, with the exception of one convention we’ve chosen to impose: To collocate Redux action creators and reducers in the same files, following the Ducks proposal. Our “actions” files look something like this:

export default function reducer(state, action) { ... };
export function actionCreator1() { ... }
export function actionCreator2() { ... }

This allows us to use default imports for the purpose of creating the combined reducer when creating the store, while still being able to used named imports in containers to import just the actions that are needed.

Reducing boilerplate in …reducers

We found that whenever we were writing reducers and action creators, we were often writing duplicate action creators and reducers, doing little more than updating a subset of the reducer’s state, for instance:

const initialState = {value: false};

export default function reducer(state = initialState, action) {
  switch(action.type) {
    case TOGGLE_VALUE:
      return {...state, value: payload};
      break;
   
  }
};

export function toggleValue(value) {
  return {type: TOGGLE_VALUE, payload: value};
}

Strictly speaking, action creators are not required since components can directly dispatch actions by themselves. However, we found that working with the more abstract action creators, allowed us to write more modular components, which would literally need no knowledge of how the reducers work or which action types are used. Instead, we simply need to pass them data and (wrapped) action creators.

Therefore, we looked into how we could simplify the reducer side of things, instead of the action creators. After a few iterations, we ended up with redux-updeep, a strongly opinionated createReducer implementation which assumes that the majority of the actions will result in deep updates of the reducer’s state. It allowed us to write the following code:

const initialState = {value: false};
const namespace = 'REDUCER_NAME';

export default function createReducer(namespace, initialState);
// That's all you need !

export function toggleValue(value) {
  return {type: `${NAMESPACE}/TOGGLE`, payload: {value}};
}

How does it work ? As mentioned before, it handles all unspecified action types in the same way, using updeep to immutably merge the action’s payload into the reducer’s current state. While it is still possible to explicitly specify action types and reducers, we still haven’t felt the need to do so!

Granted, when using redux-updeep, it becomes more complicated to compose reducers, which is idiomatic Redux. However, we’ve made it such that it is very easy to write factories that allow us to parameterize the action type’s namespaces as well as the path at which the update is merged:

export function createToggleValue(namespace, path = []) {
  return {
    toggleValue(value) {
      return {
        type: `${namespace}/TOGGLE`,
        payload: {value},
        path
      };
    }
  };
}

Then, it becomes possible to use the initial action creator deeper into a reducer state:

const initialState = {
  my: {
    deep: {
      value: false
    }
  }
}

const toggleValue = createToggleValue('OTHER_REDUCER', ['my', 'deep']);

export function toggleDeepValue(value) {
  return toggleValue(value);
}

We’re pretty happy with our current set up, so we thought that we would share it with the world. In fact, we’ve just open sourced it ! Find it on GitHub.

Handling asynchronous actions – and reducing more boilerplate

Using thunks ?

By default, Redux does not care how you handle asynchronous actions. The redux-thunk middleware is available but enforces no rules: it simply enables you to dispatch multiple actions from a single action creator. Using it, you can for instance easily dispatch loading/success/error actions in response to a Promise:

const initialState = {
  data: {},
  isPending: false,
  isError: false
};

export default createReducer('REDUCER_NAME', initialState);

export default loadData() {
  return (dispatch) => {
    dispatch({
      type: 'REDUCER_NAME/LOADING',
      payload: {isPending: true, isError: false}
    });

    get('/data').then(
      (data) => dispatch({
        type: 'REDUCER_NAME/SUCCESS',
        payload: {data, isPending: false}
      }),
      (err) => dispatch({
        type: 'REDUCER_NAME/ERROR',
        payload: {isPending: false, isError: true}
      });
    );
  }
}

This is a great start, but the code still relies on a lot of boilerplate. If you have as many simultaneous asynchronous actions as we do, and want to handle them all in the same way (e.g. how to keep track of the pending state, how to handle errors), it rapidly becomes a tedious task.

Less boilerplate, more magic!

There is a lot of middlewares in the Redux ecosystem designed to handle asynchronous actions and/or promises, but we couldn’t find one that would handle a few conventions that we had initially defined for handling asynchronous actions and asynchronous data in the components:

  • Reducer states can have several keys that will ultimately be assigned a value (let’s call those “eventual values”);
  • We want to be able to enquire whether a particular value is ready or not, without resorting to isXXX boolean flags;
  • We wanted to be able to dispatch eventual values like normal values in action creators.

So we decided to create one, the redux-magic-async-middleware, and we’ve just open sourced it! It is optimised to work with redux-updeep, and enables dispatching promises in payloads like synchronous values:

export function loadData() {
  return {
    type: 'REDUCER_NAME',
    payload: {
      value: get('/data')
    }
  }
}

The middleware will automatically extract the promises from the payload, and replaces them with eventual values. When a promise is resolved, it will trigger a success action which will update the resolved data into the reducer states, thus resolving the eventual value.

In combination with this, we have created the eventual-values library, very much inspired by another eventual values library. This library is extremely simple, but allows us to encapsulate and abstract the behaviour that we desired around asynchronous values. It allows to you write code like this:

function isReady(value) { ... };

function MyComponent({reducerState}) {
  if (isReady(reducerState.data)) {
    return <h1>Loading</h1>;
  } else {
    return <span>data.name</span>;
  }
}

What’s next ?

We’re still experimenting with those concepts and tools, and are gradually introducing flow typing in our codebase. Watch this space for more updates on how we use React, Redux and JavaScript in general!

About the author
Alexandre Meunier

Recommended Articles

Powered byAlgolia Algolia Recommend

Introducing our new navigation
product

Craig Williams

Director of Product Design & Research

Good API Documentation Is Not About Choosing the Right Tool
engineering

Maxime Locqueville

DX Engineering Manager

Building a Store Locator in React using Algolia, Mapbox, and Twilio – Part 3
engineering

Clément Sauvage

Software Engineer, Freelance