Displaying a List of Items in React, but Composed

Displaying a list of items is a challenge you will encounter in most web applications. When using a view layer library such as React, you only have to iterate over the list of items and return elements. However, often you want a couple of more features such as filtering, sorting or pagination. Not every list component needs it though, but it would be great to have these functionalities as opt-in features whenever displaying a list of items.

We are excited to open source our in-house solution at Small Improvements which handles this use case: react-redux-composable-list. In our web application, customers often need to work with lists of data while managing their feedback, objectives or other content in our system. At Small Improvements our customers range from an active user count of 20 to 2000 users. Thus it can happen that we need to display a lot of data yet have to keep it accessible for people managing it.

giphy

The requirements for each list of data are different. One list is just fine with a filter functionality. Another list mixes together selectable and filterable items. Each displayed list has different requirements. The library we are open sourcing solves all the requirements we had in-house at Small Improvements. The library is highly extendable and builds up on composition. You can come up with your own opt-in features.

Demo and Features

The react-redux-composable-list comes with the following features:

  • Filtering (AND filter, OR filter, multiple filters)
  • Selecting
  • Sorting
  • Magic Column (collapsing multiple columns in one column)
  • Pagination

There are two demo applications up and running to show the features of react-redux-composable-list.

While the former demonstrates all features in one real world example, the latter separates the examples by feature.

The Real World example shows that all features can be used altogether by composing them. To specify the opt-in features of your list components you use React’s higher order components.

const List = ({ list, stateKey }) => {
  ...
}

...

const EmptyBecauseFilter = () =>
  <div>
    <h3>No Filter Result</h3>
    <p>Sorry, there was no item matching your filter.</p>
  </div>

export default compose(
  withEmpty({ component: EmptyBecauseNoList }),
  withSelectables({ ids: [0] }),
  withPreselectables({ ids: [2, 3] }),
  withUnselectables({ ids: [4, 6] }),
  withFilter(),
  withEmpty({ component: EmptyBecauseFilter }),
  withSort(),
  withPaginate({ size: 10 }),
)(List);

You can find the implementation of both demo applications in the official GitHub repository. Further details about specific features can be found in the official documentation.

Getting Started

If you want to jump right into using the library, you should checkout the Getting Started section in the official documentation.

For instance having a list of items with the option to select items can be accomplished with the following component:

import { components, enhancements } from 'react-redux-composable-list';
const { Enhanced, Row, Cell } = components;
const { withSelectables } = enhancements;

const ListComponent = ({ list, stateKey }) =>
  <Enhanced stateKey={stateKey}>
    {list.map(item =>
      <Row key={item.id}>
        <Cell style={{ width: '70%' }}>{item.title}</Cell>
        <Cell style={{ width: '30%' }}>{item.comment}</Cell>
      </Row>
    )}
  </Enhanced>

export default withSelectables()(ListComponent);

Afterwards it can be simply used by passing a list of items and a state key to identify the table state.

import SelectableListComponent from path/to/ListComponent';

const list = [
  { id: '1', title: 'foo', comment: 'foo foo' },
  { id: '2', title: 'bar', comment: 'bar bar' },
];

const App = () =>
  <SelectableListComponent
    list={list}
    stateKey={'MY_SELECTABLE_LIST'}
  />

If you want to dive deeper into the library, you can checkout the whole documentation to learn more about the library and how to use it.

Extend it & Contribute

You can write your own enhancements and enhancers, because you have provide you with access to the library’s API. To be more specific, the library API is nothing more than action creators and selectors for the Redux store. All the state that is managed for the tables is organized in a Redux store. You will find everything you need to know about the API in each documented feature. In general, the documentation is a good place to get started and to read up everything about all the features.

We would love, if you would give it a shot and give us feedback about it. In addition, we welcome you to make contributions to the library.

Resources

Onboarding your Team to React + Redux

react redux team

Eventually the time will come when your team wants to use React + Redux for their frontend stack. We made that commitment some time ago at Small Improvements – we never had to regret it. As we come from an Angular 1.x frontend application, we needed to decide between React (+ ecosystem) and Angular 2. Staying with Angular 1.x was no option for our three teams. We saw too many benefits in other solutions like React e.g. to embrace functional programming. In the end we decided to go all the way with React + Redux, since most of our developers used it already in their side projects.

The article should give other teams or even companies some learnings and insights to have a smooth onboarding to the React + Redux ecosystem. While some learnings are applicable to React + Redux, others might be general insights about migrating to another technology.

React before Redux

In the beginning not everyone is familiar with React and its ecosystem. Give people time to understand, to experiment and to exchange thoughts. Introduce React without a state management library. Make use of the React lifecycle methods and internal state management (setState) to teach React itself. After a while you will want to introduce a state management library. It’s easy to get around an external library in smaller side projects, but it isn’t when you are contributing to a larger code base. In general don’t introduce a state management library when you don’t know the problem it solves for you.

Introducing Redux

In Angular state management got messy for us. Sharing state between components, watching state changes of components and services, storing server data – it was pretty soon a chaos. We knew the flaws of state management in Angular when the Flux pattern evolved. Perhaps that’s the cause why so many developers at Small Improvements got hooked by React + Redux eventually.

Don’t overengineer Redux

Once you started using Redux as state management library, don’t overengineer it. In the beginning the Redux ecosystem itself can be overwhelming. Moreover the way you deal with state management in Redux is different than what a lot of people are used to from the past. Again, like in React, give your team a chance to understand Redux. Not everyone will be already familiar with the overarching functional programming principals behind it.

Already one little library like redux-actions masks the excellence of Redux to stick to vanilla JavaScript. In redux-actions people might never experience plain actions and action creators. The functionality createAction camouflages some basic usage of Redux. Same goes for the handleActions helper function. People might never experience a plain Redux reducer which itself is plain JavaScript reducer function.

We love to use these little enhancements in Redux, but we made the mistake to introduce them too early. Nowadays it’s easy to npm install a package. Speak with your team about new packages. Don’t take from them the opportunity to learn it themselves.

Understand it, before you use it

Learn React before using Redux. In react-redux you use the connect functionality to literally connect the Redux store to your React components. The Provider component at the root level makes sure that the store is passed as context to the underlying components. Thus you can retrieve the store state in mapStateToProps and pass dispatchable actions on the store to your components in mapDispatchToProps.

But what is the connect doing there? It’s too easy to simply call it magic. When learning React before using Redux, you can make sure that everyone in your team understands the concept of higher order components (HOC). After that everyone has the chance to reproduce the underlying mechanics of connect in react-redux. Eventually everyone is aware of the hidden Redux store in the React context.

Asynchronous Actions

We decided to use redux-saga as we had to introduce asynchronous actions. In our case some people already used it before and felt that it is a great match. After some time using it, we don’t regret to handle our side effects with generators.

But what is the best approach to introduce asynchronous actions? Not everyone is aware of generators after all and it might add yet another level of complexity. Redux-thunk is a great way to begin with asynchronous actions. It allows you to dispatch delayed actions. When the whole team feels comfortable with asynchronous actions, you should decide whether you want to experiment with another solution. The way to deal with asynchronous actions should be a recurring discussion topic until you make the final decision. Otherwise you will delay the decision and end up with bigger refactorings of your asynchronous layer.

Normalize your data?

We don’t normalize our data, even if we were aware of the aspect to keep a flat state in Redux. It was no unconscious decision to keep a deep nested state. Since we already have a large Angular 1.x application, we are used to most of the data structures. In our case it would expand the gap between the two worlds, because we would have to get used to two different data structures. Once we introduce new data structures, we keep the state flat from the beginning. We are not sure yet whether it was a bad (unconscious) decision. Still we feel comfortable to keep our deep nested state immutable by using ES6 spread operators. Moreover reducer tests with deep-freeze help to ensure immutability.

Feature Folders

In the beginning we had a technical folder separation. Everyone is used to it from React + Redux tutorials. We had folders for reducers, actions, components, constants etc. Very early we noticed that the approach would never scale with independent teams. We decided to have feature folders. Now we have packages with clear boundaries. Take for instance a Table component package:

--Table/
----index.js
----components/
------Table/
--------index.js
--------container.js
--------presenter.js
--------style.less
------Cell/
--------index.js
--------presenter.js
--------spec.js
------Row/
--------index.js
--------presenter.js
--------spec.js
----ducks/
------index.js
------filter
--------index.js
--------spec.js
------sort
--------index.js
--------spec.js
------select
--------index.js
--------spec.js

One index file gives an entry point to each package. The ducks index file still exposes all necessary action creators and reducers. When another package wants to dispatch a Table action, it has to import it from “Table” and not from “Table/ducks”. The package has clear boundaries.

Ducks Everywhere

“I saw you are using ducks?” Yes. We decided to use them when we introduced feature folders. The advantage is to have everything in one place. But once you introduce ducks, you should decide on best practices to keep the duck files tidy. Standardize your naming for reducer and action creator functions to distinguish them.

Moreover we noticed that ducks don’t scale very well for us. The lines of code grew very fast. That’s why we decided to split up the ducks responsibilities in smaller domains, like you can see in the ducks folder for the Table in the example above.

What about boundaries to legacy frameworks?

It’s silly that we call Angular 1.x already legacy, but that’s JavaScript today. Still we had to figure out how to connect both worlds.

ReactDOM.render() is all you need to have a React component tree in Angular. Moreover you can simply use the react-redux Provider component to pass the imported Redux singleton store as context to the component tree. You can dispatch actions on the store and get the state from everywhere, since you only need to import the store in your non React world.

The other way around we use a helper to render Angular components in React. Once you have a large code base with complex non React components, you can’t easily rewrite them all at once. That approach ensures us a stable migration from Angular 1.x to React. We can still reuse Angular components. Once we refactor one component from Angular to React, we can easily exchange the component in one place.

What about a synced cache to the legacy framework?

In the beginning we experimented with Relay to facilitate caching of our backend data. Even more we had attempts to make Relay independent of React to use it in Angular as well. But very soon Relay felt like a foreign object in React to us. We stopped the experiment to use Relay + GraphQL and remained with using our RESTful solution.

Still we had to figure out how to cache the server side data in our single page React + Angular application. Since we already used an own store architecture in Angular, we synced the stores to the Redux store. Everything we implement in the future uses the Redux store, but our old Angular pages still get the cached data from our store architecture.

Moreover we introduced ladda to cache requests to our API. It’s an in-house solution by one of our developers, which will get open sourced properly. Ladda introduces a JavaScript data fetching layer with caching without dependencies. You can easily make requests in Angular or React.

Hack & Tell

You read a lot about giving people time to understand the ecosystem properly. Your whole team is sitting in the same boat when introducing something novel. Everyone tries to accomplish a scalable and maintainable code base in the new ecosystem. At Small Improvements we are having weekly Hack & Tells to exchange our recent gatherings. We share learnings to get a mutual understanding of doing things in React + Redux. In general those Hack & Tells don’t apply necessarily to one technology.

Knowledge you could exchange in a weekly Hack & Tell:

  • best practices
  • patterns
  • decisions like naming, folder structure etc.
  • reusable components / feature packages
  • new npm modules which solve a real problem in your code base
  • recent pull requests

Perhaps once a week isn’t even enough to exchange knowledge in a whole new ecosystem. Our code base is scaling well, even though we feel that we could refactor all the time. We don’t regret the step to migrate from Angular to React and its ecosystem.

Hack It, Ship It!

Small Improvements conducts Hackathons every few months which usually involves two days of hacking on an experimental project. Hacking doesn’t imply that it’s a “developers-only” affair either; other departments at Small Improvements like Customer Success and Marketing also get a chance to get experimental too. Hackathons usually give us a chance to really get creative about novel concepts and ideas for the product and company. It is about coming together as people to grow as a team – something that we at Small Improvements value as an important part of internal culture.

From Hackathon to Ship It Week

Being creative with innovative ideas is great, but how would we actually Ship It? We introduced the Ship It Week as new concept to extend our occasional Hackathons from 2 days of hacking to 5 days more serious hacking! In the first two days we had our regular experimental Hackathon, but afterwards had to decide which projects were feasible to “ship” until the end of the week. In the context of a feature in the Small Improvements feature, ship would mean: going live for customers!

Continue reading

React Europe 2016

In the past, we at Small Improvements have sponsored various Angular Conferences and Meetups. So how did our developers end up at React Europe? Let’s backpedal to understand the story behind that.

Small Improvements and React!?

At Small Improvements we decided in early 2016 to shift towards React instead of working to migrate to Angular 2. Internally a lot of our developers had had great experiences with React and its ecosystem in their side projects. We wanted to keep up with evolving technologies rather than being stuck with Angular 1. Even though we already have a large frontend codebase in Angular, in the past 6 months we’ve also been able to implement new components and pages in React. We are confident that it was the right choice for the future. We also experimented with Relay + GraphQL, but it felt too premature to use these technologies at this point.

DSC03847

Continue reading

JSUnconf in Hamburg

We were proud to once again be a sponsor at last week’s JSUnconf in Hamburg. The conference, that centers around the latest and greatest in JavaScript, is truly the “unconference” in both content and overall execution.

It’s a completely non-profit conference built for the community, by the community.  As the event grows year over year, we’ve found that it’s an incredible place for idea sharing, networking with other developers and sharing a positive contributions to the JavaScript ecosystem.

A few of the things that really stood out to us were:

Continue reading