5 top tips for developers using ReGraph & React

We spend a lot of time making our data visualization development toolkits as easy to use as possible, without compromising on performance and capability. Our expert support team – run by the developers who created the products – are also on-hand for any unexpected road bumps or behaviors.

It’s always exciting to see what incredible graph visualization apps developers have built with our tools. We welcome feedback, and listen carefully to experiences and requests so we can make our products even better.

A wall map with flagged pins showing the customers we have across the world
Developers around the world use our graph and timeline visualization SDKs to build fantastic apps

Our graph visualization SDK, ReGraph, is tightly coupled with the popular development library, React. As a result, we lean heavily on some core design decisions made by the React library and ecosystem. And because ReGraph makes it so easy to build compelling applications, it’s sometimes easy to forget the coding basics.

Even the most proficient developer makes mistakes sometimes. We’re only human!

Whether you’re a new React developer or an experienced pro, there are a few things to steer clear from. In this blog post, I offer tips for developers using ReGraph on how to avoid five common issues.

1. Automatic layout woes

Most articles like this start with a small pitfall and build up to a climactic error – not us. We’re starting with the number one mistake made by developers using the Chart component from ReGraph.

ReGraph includes a strong graph layout engine with a number of powerful layouts with different characteristics. Think of them as different superheroes, each with their own special skill. The choice of layout is configured using the layout prop found on the chart itself. We want to pass the prop something like this:

	{ name: ‘organic’, tightness: 6 }

This gives us a nice clear pattern that’s easy to understand and helps reveal underlying structures.

A large ReGraph network visualization showing connected entities in dark mode in an attractive organic layout
ReGraph’s organic layout: the most powerful, high performing layout in the SDK

ReGraph runs our efficient organic layout by default, because it’s the most powerful, high performing choice for almost all scenarios (find out why in our post on faster, smarter, adaptive organic layout). But there are other specialist layouts available to suit specific types of network.

Let’s say I want to try out the lens layout to find well-connected nodes in my largest dataset more quickly. I may update my Chart component as follows:

<Chart items={items} layout={{ name: ‘lens’ }} /> // don’t do this!!

At first this appears to be working correctly – your layout will look something like this:

A large ReGraph network visualization showing connected entities in dark mode in an attractive lens layout
This automatic layout pushes highly-connected nodes to the center resulting in an attractive ‘lens’ view

Unfortunately as you add more logic and complexity to your application you’ll find the layout runs again. So what’s gone wrong?

Our Chart is part of a React component that’s being rendered onto the page. When something changes in my application – this may be a Chart event like a click or something external to the Chart – a re-render occurs.

At this point the Chart component receives the layout prop again and thinks you’re requesting a fresh layout. This is because to the component it’s a brand new object.

There are a few fixes for this issue. My favorite is to use a hook (check out harnessing hooks in your ReGraph code) to put my layout in state:

const [layout, setLayout] = React.useState({ name: ‘organic’ })
<Chart items={items} layout={layout} />

This makes the layout object consistent across renders. We have the added bonus of getting a setLayout function to make it easy to update my layout in the future.

2. Issues with updating items

ReGraph is inherently visual and interactive. Once you’ve grasped the basics of the library and built your first data view it’s likely that you’ll want to update the styles in your application to reflect an action. You can customize every interaction to match your users’ expectations, and choose from a huge select of styling options.

Let’s assume you want to highlight all nodes with a particular name using a halo. This creates a halo-effect ring to draw attention to key items, events or alerts. They’re also easy to animate for added impact.

An animated halo makes it easy to spot alerts in an IT network

By now you’ll be familiar with putting your Chart props in state for efficient management of updates. You’ll likely have an items object in state like this:

const [items, setItems] = React.useState( { node1: { color: ‘red’} } )

So now I want to add a halo to a node of interest. Having learned from my layout mistake, I might write something like this, taking advantage of the useEffect hook to run setItem if match changes.

 React.useEffect(() => {
    setItems((items) => {
      const newItem = { ...items[match] };

      const newHalo = { radius: 50, color: 'red', width: 5 };
      newItem.halos.push(newHalo);

      items[match] = newItem;
      return newItem;
    });
  }, [match]);

To get access to the current state of items, I’m passing in a parameter to my setItems function. I’m also using the ES6 spread operator to take my old item.

So then I update match and… nothing happens. What gives?

ReGraph relies on the concept of immutability so that only updates that definitely need to happen, happen. As well as boosting performance, this helps developers avoid bugs in the long run and makes it easy to implement undo/redo stacks in your application.

The spread operator (…) is a simple shallow clone of the item in my items object. This means the nested objects inside the item remain the same, despite my attempts to sever them from the original item. ReGraph receives this and, because it’s a mutation of the existing item, assumes there’s no action required.

Again, there are several ways to avoid this issue. You could:

  • use Object.assign({}, item, update})
  • use the lodash merge function – if we pass an empty object as the first argument this also does a deep clone. ReGraph has a third-party dependency on the popular lodash utility library, so it’s in your environment already. This could be the easiest approach.
  • use a library like immer or Redux Toolkit to be even smarter with your state. This is the best solution if you’re prioritizing performance. (Also see my article on React Hooks vs Redux.)

Here’s an example of the lodash approach in action. I’m using mergeWith to make sure our nested halos array is concatenated.

import { mergeWith, isArray } from ‘lodash;

const concatArrays = (objValue, srcValue) => (isArray(objValue)) ? objValue.concat(srcValue) : undefined)

  React.useEffect(() => {
    setItems((items) => {
      const newItem = { ...items[match] };

      const newHalo = { radius: 50, color: 'red', width: 5 };
      items[match] = mergeWith({}, items[match], { halos: [newHalo] }, concatArrays);

      return newItem;
    });
  }, [match]);

3. Avoiding expensive operations

Here’s something I learned only recently: you may be accidentally running expensive processing on each and every render in your React application.

What do I mean by this? I’ll use examples to explain the two areas to watch for this issue.

Style conversion function issue

It’s common to have a style conversion function in your application to do some final front-end styling configuration before the nodes and links are presented to the user. This may be influenced by a theme toggle configured in your application.

const [items, setItems] = React.useState(initialItems())

const convertItems = (sourceItems) => {
  const result = sourceItems;
  // complex data mapping code here
  return result;
};

const styledItems = convertItems(items);

<Chart items={styledItems} />

Can you spot what the issue is here? It’s like our layouts issue again: our convertItems function is being re-defined on every render. We can solve this by relying on the useCallback hook to cache or memoize our function. Read more about that in React’s documentation.

But why is this function in our component at all? Surely it should be defined elsewhere so that our component knows that it’s unchanged between renders?

One potential reason is that your convertItems uses another variable or state defined elsewhere in the component. If this is the case, you’re almost certainly going to avoid issues by rewriting your function to use useEffect to track side effects and changing variables more effectively.

Costly initializations

The second example features our friend, setState. We can provide the initial state for a variable as an argument to setState like this:

const [items, setItems] = React.setState(initialItems());

This call means that initialItems is actually called every time your component renders. You can use lazy initialization to avoid this – simply provide a function to call instead of executing initialItems in place:

const [items, setItems] = React.setState(initialItems);

It’s a simple change but it may save a lot of time.

As your application gets more complicated it’s worth checking every function in your component to see whether re-renders or computations can be cached or memoized.

4. Use the right events

If you want a click to trigger an action, you may first reach for the click events provided by the ReGraph API. This sounds logical but there’s a better way to handle interactivity.

The problem with pointer events is that no action truly happens in isolation. The click event you’re binding to is likely to be related to the selection behavior you want to use with your chart.

ReGraph provides a more robust method of handling interactivity based around the idea of a selection on chart change. The onChange event fires whenever anything changes in the chart, and the selection argument gives us exactly what we need to track selections in state. This means we can use our trusty useEffect hook to update state in an application to reflect the currently selected items.

This isn’t really a pitfall – it’s useful behavior in ReGraph – but onItemInteraction makes it much easier to update node and link styles when a user hovers or selects a node. This means we can avoid the item state management from our first tip in a number of scenarios.

Subtle but effective ‘on hover’ visual clues help create an interactive graph visualization

Another example of this is using tooltips in your application. The onViewChange event fires more frequently than the onHover event and is perfect for making sure your custom tooltip follows your cursor when hovering over a chart item.

For other ways to customize interactions efficiently, see 4 easy styling options for interactive graph visualization.

5. Not changing the defaults

When you’re building an application using ReGraph, there’s practically no limit to the interactivity and styling configurations.

An example from ReGraph’s big data demo, one of several showcases available from the SDK

But two things that developers and designers often miss is the default selection style and chart navigation controls.

Changing the selection colour is as simple as setting the relevant chart option prop:

{ selection : {color: prettyBrandColor } }

The theme chart option is a similar setting:

{ controlTheme: ‘dark’ }
A network visualization showing mafia connections with customized controls and select behavior
Choose the style, mode and position of navigation controls, and customize select behavior

Code your best graph visualization app

I hope you’ve found some useful tips here to make your ReGraph and React experience go smoothly.

As a developer creating apps with the ReGraph SDK, have you experienced any of these issues? Did you do something similar to resolve them? What would you add to this list? Feel free to share your tips!

Haven’t tried ReGraph yet? If you’re looking for a simple data-driven API for adding graph visualizations to your React apps, request a free trial.

More from our blog

Visit our blog

Registered in England and Wales with Company Number 07625370 | VAT Number 113 1740 61
6-8 Hills Road, Cambridge, CB2 1JP. All material © Cambridge Intelligence 2021.
Read our Privacy Policy.