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

Our journey migrating 100k lines of code from AngularJS to React (Chapter 1)

Web

Intro

This is the first post of a series explaining the story and technical learnings we had from starting to migrate from AngularJS to React. Check out the github repo for examples and the full code.

Our frontend story so far

At Small Improvements we’re aiming to make meaningful feedback available for every employee in every organisation. This also implies that we provide the best experience for our users. Therefore we’ve been on the front line to adopt AngularJS over Wicket, and started to rewrite our core features in AngularJS back in 2012. We saw the great potential in having a dynamic single page application.

In 2014, when the Angular team announced Angular 2, we already had a very large application and had gained a whole lot of knowledge using Angular. We were worried and excited at the same time. We faced a lot of challenges scaling Angular 1 and implementing best practices while moving fast.

In 2015 we sent almost all developers to AngularConnect in London, expecting the Angular 2 BETA release. Two of our developers gave a talk to share our approach to and learnings from writing a huge AngularJS application. We came back with the impression that Angular 2 was still very unstable and no clear migration strategy seemed to be available.

anguarconnenct.jpg
The Small Improvements Team in London at AngularConnect

Testing React plus Relay and GraphQL in the field

Our CEO has a strong engineering background, so he’s very open to play around with new technology and loves hackathons and ship-it weeks. That’s why he was very open to giving React (with Relay and GraphQL) a chance. As a company, our approach to evaluating a new technology is to have one of our dev teams make an initial tech spike. In this case Team Green decided to experiment with the novel technology in the field by coding a prototype for a new feature in the new tech stack.  We found React extremely promising and it solved a lot of challenges we had with Angular 1. Relay was cool, but lacking some core features at that time, such as support for invalidation or lazy loading of expensive fields.

Also adopting Relay would mean a complete buy-in from our whole stack, Frontend and API layer, due to the dependency on GraphQL.

So to sum it up, the outcome was: React: OMG!, Relay: Cool, but…

Our Reasons to go with React

  • Easy to write: It’s closer to vanilla JavaScript and components come without any boilerplate configuration
  • Great use for atomic components. In contrary to AngularJS where every scope is “expensive”
  • Easier to understand, React is the view library and has a slim API
  • Designed with performance in mind: Concept of virtual DOM
  • Attractive for recruiting, new technology attracts passionate developers, because they are keen to adopt new technologies
  • New challenges for the dev team, learn and grow!

When we used it for a large feature – a new Activity Stream – it multiplied our investment, due to an unclear focus. We were shifting between trying out the new technology and building first iterations on the feature.

Lessons learned

Use a smaller feature as playground when experimenting with a new technology.

The migration strategy

Now that we’ve decided to move from AngularJS to React, we saw two options for a migration strategy: A complete rewrite of our frontend or a slow transition. Let me rephrase that: We saw one option: A smooth and focused transition. Nobody wanted to spend months rewriting our whole application, although that would have been a fun argument with our CEO. At Small Improvements, we have a strong customer centred culture, so we didn’t want to slow down too much on our mission. Additionally it is a high risk to rewrite everything with a technology that nobody is experienced with.

Each week all Software Developers at Small Improvements meet for a developer exchange meeting. That’s the place where we share learnings, discuss ideas but also decide on larger undertakings. In this case we discussed and decided on the idea for the migration strategy a sub team of developers has developed and presented.

The basic idea

A frontend application is built like a tree, since HTML documents imply a structure of nested HTML elements. Modern web applications are structured in nested components. A simplified mock of an application displaying a list of comments may look like that:

Screen Shot 2017-01-24 at 09.50.02.png

The corresponding component tree looks like this:

Screen Shot 2017-01-24 at 09.50.06.pngWe looked at how complex it would be to replace and rewrite this tree.

screen-shot-2017-01-24-at-09-50-34

The main Application component is hard, it usually is wired up with complex logic like routing. Similar the Navigation component. The routing is tightly coupled with the main components and in case of AngularJS central piece of the framework. A NavItem is easier, it displays a link and has some trivial logic like “am I active” and displays a link with text. The content part of our app consists of a sub tree displaying a list of comments. The ComponentList is trickier, since it is hooked to the data layer and may contain state like: what item is selected etc. Again we see the Comment is the easiest part of that tree, basically rendering a Comment and handling user interaction. The Text component for instance is simply responsible for rendering the text. That component is easiest to re-write in another technology.

Our conclusion was that the further down the component tree you go, the easier it gets to replace components. With that in mind, we defined guidelines and looked at requirements for that migration strategy.

Guidelines

How to tackle new features?

We wanted a full buy in, so we defined our first guideline:

  1. Every new feature will be built in React & Redux.

How to tackle existing code?

  1. If possible start to migrate leaf-first up to a whole component tree until you hit the routing module.
  2. If you touch old code/ components, estimate how much it would cost to rewrite it, if less than 30 minutes, rewrite, else get a second opinion.

How to migrate common UI components?

The basic building blocks of an application are generic, reusable UI components, like Dropdowns, Buttons, Forms etc. Those are necessary to build new components with React.

  1. Re-write generic UI components when you need them, and let other devs know that they now exist. Use that chance to improve the design/ UX.

Requirements

  • Component based architecture
  • Angular Directives structured as container/ presenter components, read more here
  • Separation of concerns/ View/ Logic/ Service/ Communication layers and Injectable actions to encapsulate side effects like http calls etc

Fortunately our frontend design already fulfilled the requirements. If you want more information on how to design and structure your application watch our talk How to design large AngularJS applications that scale from AngularConnect or Refactoring To Components by Tero Parviainen.

Building bridges

We found that it was easiest to start by replacing the leaves of our application component tree. The missing piece was a bridge between the “old” world and the “new” world. Meaning AngularJS and React, in our case. How can we use React to render the Text component and get it’s data from an AngularJS component?

Rendering React within AngularJS

A React component is, well, just another UI component. It gets data and actions via props and is rendered to the DOM. It is responsible for internal state and handles user interaction. So a simple concept of our bridge could be an AngularJS component working as thin layer with the responsibility to pass on data to the React Component.

Let’s aim to answer our first uncertainty: Can we use an AngularJS component to render a React component?

This is our AngularJS comment component:

module.exports = angular.module('ngReactExample.comment', [
]).component('comment', {
    bindings: {
        comment: '<',
    },
    template: '{{ $ctrl.comment.text }}',
    controller: function() {
    }
});

Our React version of a comment looks like that

const Comment = (props) => {
    return (
         { props.comment.text }
    );
};
export default Comment;

The React component is rendered to the DOM by calling:

ReactDOM.render(<Comment />, element);

Let’s try to call this within an AngularJS component:

import Comment from './Comment';

module.exports = angular.module('ngReactExample.comment', [
]).component('comment', {
    bindings: {
        comment: '<',
    },
    controller: function() {
        ReactDOM.render(<Comment />, $element[0]);
    }
});

It works! This is the simple yet powerful starting point from where we can now build our AngularJS – React bridge. The elegant part is that we don’t need to mess around with DOM node ids or use the DOM API to query the element we want to render React to. We can directly pass the reference to the AngularJS element. You might have noticed a little detail – at the moment we’re only rendering the React component when this component is initialized. In a dynamic app we want dynamic components. So we want to trigger the rendering whenever the component changes. To achieve this we can use the lifecycle method $onChanges.

import Comment from './Comment';

const render = (element) => {
    ReactDOM.render(
        <Comment />,
        element
    );
}

module.exports = angular.module('ngReactExample.comment', [
]).component('comment', {
    bindings: {
        comment: '<',
    },
    controller: function($element) {
        const $ctrl = this;
        $ctrl.$onChanges = () => render($element[0]);
    }
});

Now whenever our AngularJS component receives changes we’re redrawing the React component.

With this working we can tackle the next question: How we can pass data down to our React component?

Passing data from AngularJS to React

In React we use props as interface to pass data to a component. An AngularJS directive receives inputs via bindings, so we will get the comment data from an outside component and pass it down to our React component. The full working bridge looks like this:

import Comment from './presenter';

const render = (element, props) => {
    ReactDOM.render(
        <Comment { ...props } />,
        element
    );
}

module.exports = angular.module('ngReactExample.comment', [
]).component('comment', {
    bindings: {
        comment: '<',
    },
    controller: function($element) {
        const $ctrl = this;
        $ctrl.$onChanges = () => render($element[0], { comment: $ctrl.comment });
    }
});

Fixing the possible memory leak

As described here React will not automatically clean up the components which can lead to a memory leak. We can use the lifecycle hook $onDestroy() of our AngularJS component to unmount the React component.

import Comment from './presenter';

const render = (element, props) => {
    ReactDOM.render(
        <Comment { ...props } />,
        element
    );
}

module.exports = angular.module('ngReactExample.comment', [
]).component('comment', {
    bindings: {
        comment: '<',
    },
    controller: function($element) {
        const $ctrl = this;
        $ctrl.$onChanges = () => render($element[0], { comment: $ctrl.comment });
        $ctrl.$onDestroy = () => ReactDOM.unmountComponentAtNode($element[0]);
    }
});

Voila! We’ve successfully passed data from AngularJS to a React component.

Completing the bridge from AngularJS to React

We’ve now found a way to wrap a React component with an AngularJS layer, so we can hook it up to the rest of our application.

This is a great starting point and a good proof of concept. Our current bridge is an interesting evolution of this first spark. In the next posts we will go more into technical details, also answering the question what we do, when the AngularJS component get’s destroyed, and more topics.

To be continued…

A sneak peak into the next chapter where we’ll have a closer look at:

  • Using AngularJS services in React
  • Improving the AngularJS-React bridge to work with Hot Reloading and avoid unnecessary re-renderings
  • Rendering AngularJS components in React

Stay tuned! 😉

Thanks for reading and if you have any questions or feedback, don’t be shy and reach out! @sfroestl If you liked the post, please share!

About the author

green3-1024x683

Sebastian Fröstl

Team Lead. Software Engineer. Trainer. Coach. Speaker. Devoted to Personal Development. Organizer of @angular_berlin.
@sfroestl
sebastianfroestl.de

Resources

Onboarding your Team to React + Redux

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.

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.

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

Angular 2 and Relay

Keeping client and server in sync can be tough when building a single page app, and involves tons of boilerplate code. In this write up we’ll combine the best of two worlds by hooking up Angular 2 with Relay, making your workday much more fun and productive!

This technology is so promising, that we’re running an internal experiment at Small Improvements to evaluate its benefits. We gave a talk about Angular 2 and Relay at NG NL 2016 and this is a writeup of the content. (The video of the talk will be published within the next weeks.)

Continue reading

Did you miss ng-europe 2014? Here is what we learned!

First of all ng-europe 2014 was a really great conference. Many high level talks, the core AngularJS team really accessible and awesome attendants. Many thanks to the organising team who made this event happen. We were proud to be among the sponsors!

There was so much to learn and to take home from this event, but these are our top learnings. We hope this post gives you a good entry point to catch up with some of the missed learnings.

General impression

The AngularJS team is heading for big and enthusiastic goals! Angular continues to be on the top of SPA frameworks

Angular JS 1.3? Update…? Yes, now, NOW! 🙂

“We’ve just shipped AngularJS 1.3 — the best Angular yet.” ~ Jeff Cross and Brian Ford

AngularJS 1.3 introduces many features and improvements concerning performance, forms and ARIA support.

 

Continue reading