react-loadable and TypeScript error: Property ‘render’ is missing in type ‘{ loader: () => Promise

The react-loadable library provides an excellent way to load React components on demand while displaying a temporary view until they finish loading (such as a spinner animation). Recently, I tried using react-loadable with TypeScript, and the compiler reported a strange and unexpected error. Here’s the journey I took figure out what was wrong, and how I fixed it.

You can see that my code below is very simple, but I was getting an error, for some reason:

import * as React from "react";
import * as Loadable from "react-loadable";
import MyLoadingComponent from "./MyLoadingComponent";

const LoadableMyComponent = Loadable(
	loader: () => import("./MyComponent"),
	loading: MyLoadingComponent

The full compiler error appears next (with a modified file path to make it more readable):

Argument of type '{ loader: () => Promise<typeof '/path/to/MyComponent' is not assignable to parameter of type 'Options<any, typeof '/path/to/MyComponent">'.
  Type '{ loader: () => Promise<typeof "/path/to/MyComponent' is not assignable to type 'OptionsWithRender<any, typeof '/path/to/MyComponent'.
    Property 'render' is missing in type '{ loader: () => Promise<typeof '/path/to/MyComponent'.

In my editor, the error begins at the opening curly brace { and ends at the closing curly brace }, so it’s clear that there’s something wrong with the options object that I’m passing in. However, it was not immediately obvious to me why the TypeScript compiler didn’t like my object.

The error says that a render property is missing. My first thought was that I was missing a render() method in a component somewhere. However, both MyComponent and MyLoadingComponent are React components with proper render() methods defined.

The code for MyComponent.tsx appears below:

import * as React from "react";

export default class MyComponent extends React.Component<any>
		return <div>MyComponent has loaded</div>

And here’s the code for MyLoadingComponent.tsx:

import * as React from "react";

export default class MyLoadingComponent extends React.Component<any>
		return <div>Loading...</div>;

As you can see, render() is not missing in my components. So where exactly should a property named render be defined?

After reading through the react-loadable documentation, I figured out that the options object can be one of a few different variations, and one of them requires a render property. The TypeScript compiler thinks I’m trying to use the variation where this property is required…

…but I’m not.

According to the following comment from the type definitions, I need to meet the following requirements to make the render property optional:

If you do not want to provide a render function, ensure that your function is returning a promise for a React.ComponentType or is the result of import()ing a module that has a component as its `default` export.

You can see that the function that I pass to the loader property should work. I’m loading a module containing MyComponent as the default export, and it extends React.Component.

loader: () => import("./MyComponent")

After some trial and error, I determined the real problem. The React component that I pass to the other option (loading) isn’t accepted because I typed its “props” as any:

class MyLoadingComponent extends React.Component<any>

According to the type definitions for react-loadable, I should use ReactComponent.<Loadable.LoadingComponentProps> instead. I would have expected the TypeScript compiler to figure out that Loadable.LoadingComponentProps can be assigned to any, but I guess not.

Anyway, here’s how I should have defined MyLoadingComponent instead:

import * as React from "react";
import * as Loadable from "react-loadable";

export default class MyLoadingComponent extends React.Component<Loadable.LoadingComponentProps>
		return <div>Loading...</div>;

It took me quite some time to figure out what exactly was wrong with my code. The TypeScript compiler was fixated on that render property, which led me down the wrong path. I wish the compiler would have realized that my React.Component was using type any for its props instead of the Loadable.LoadingComponentProps. I wonder if there’s a better way to define these options in the type definitions so that the wrong type for props would have taken precedence over the missing (but not really) render property?

Migrating an Apache Flex app to React: Milestone 1

Last year, I mentioned that I’m migrating an Apache Flex application to JS/HTML/CSS. At the time, I was still very early in the process. I knew that I’d be using TypeScript and React by that point, but I was still evaluating various UI component libraries. In addition to learning and prototyping, I also had to port a lot of code that wasn’t part of the UI, so I haven’t had much to share about my progress until recently.

Yesterday, I deployed the first bit of code from the new version of my app Logicly to production. The original codebase included something that I call the “Logicly Viewer”, which is a limited version of the project that can be embedded in HTML to load a file in read-only mode. I created it so that I could embed a real circuit simulation into the documentation to help make various concepts more clear. Unlike the main editor, nothing in Logicly Viewer can be added, deleted, or dragged around. However, you can interact with certain components to update the simulation and watch signals propgate. I knew right away that the Logicly Viewer would make a perfect first milestone because I wouldn’t need to implement Logicly in its entirely. All of the work on this milestone will act as a foundation for future milestones, and I get to deploy something to production that real users will be able to see today while I continue to work on the rest of the project. That really helps keep me motivated!

I’ve embedded the Logicly Viewer below, with a simulation of a half adder:

(Click/tap the switches on the left side to see the simulation update)

Logicly simulates logic gates, which are low level constructs in computers and electronics that perform boolean math (operations on 1s and 0s). The simulation keeps track of the current state of a circuit, which includes the signal being output from each logic gate and where that signal gets passed next. It was relative easy to port the simulation from ActionScript to TypeScript, since the languages are so similar. However, I wanted to avoid introducing any bugs, so I ended up writing a ton of automated tests at the same time. The original implementation of the simulation in ActionScript didn’t have any tests, so writing them took a non-trivial amount of time because I had to start from scratch. It was worth it, though, because it caught a few bugs that I had accidentally introduced into the new TypeScript version.

Once the simulation was up and running, I started working on the “wires” that can be dragged between components to connect things in the simulation. You can see them in the embedded viewer above — they’re the bezier curves that are drawn between components. Implementing these as React components was an interesting challenge because it requires more access to the HTML DOM than most people using React typically need. Additionally, React’s typical data flow doesn’t play well when many components need to interact with each other from various levels of the tree. I ended up using Redux so that I could use its Provider component to connect the components with data. As for visuals, I ended up using SVG. It’s really nice to be able to use SVG right alongside HTML with JSX.

The code below basically shows how I use my wires library in React/JSX:

	<WireSurface wireFactory={this.createWireRenderer}/>

The WiresProvider is a Redux Provider component with an internal store, and it passes the data down to any components that are interested. The WireSurface is where the wires are drawn. As I mentioned, I implemented the wires as SVG bezier curves, but the WireSurface supports custom implementations that draw wires differently, if needed. The wires get the data about the connections from the WiresProvider. The components added to the <div> that appears after the WireSurface are the logic gates and other components that get displayed by the viewer/editor. These may contain child components called “terminals” where the wires start and end their connections.

The logic gates and other components are also drawn using SVG. I was able to export SVG files from my original vector art created in Adobe Animate CC. I made a few tweaks to simplify the generated SVG, but the output was actually pretty decent overall. Between JSX and CSS, modifying paths and colors in SVG at runtime turned out to be very easy. I’m excited to explore SVG further because I feel like modern web apps could really benefit, but it seems to be underutilized today.

Obviously, the Logicly Viewer needs to be able to open files created by the full version of Logicly. The underlying file format is XML, but it’s also compressed using the deflate algorithm. I ended up using the pako library to inflate the data to get the raw XML string, and I used @rgrove/parse-xml to parse the XML into JS objects. Originally, I had worried that file parsing would be a major pain. However, this ended up being one of the easiest tasks that I worked on in this project! The @rgrove/parse-xml library produced a very easy to understand object tree.

What’s next?

In the next milestone, I will start implementing the full editor with things like drag-and-drop and selection. In fact, I already have some of the drag-and-drop features implemented, since I wanted to be sure that was easy enough to implement with React before I committed. I’ll be using the excellent react-dnd library.

The next thing that I will deploy to production will be the web demo of Logicly that allows people to try the app in their browser. It currently runs on Adobe Flash Player, and #Flash2020 looms ever closer. This web demo doesn’t support saving files, printing, or opening multiple files at the same time, but everything else works the same way as the desktop app. It’ll be more work than the Logicly Viewer, I think, but it’s also another subset that allows me to deploy more to production before the whole project is completed. After that, I’ll start digging into Electron to build desktop apps for Windows, macOS, and Linux. Then, Logicly will be ready for the future, and I can explore new possibilities, like mobile apps, Chromebooks, and more!

How I promote my projects on social media

In the past, when releasing a new version of one of my projects, I would write up a long announcement and post links on social media. It would get retweeted and shared, and that was basically all of the promotion that I did. After that, I’d go silent for a few months as I started working on the next version of my project. I’ve always wanted my projects to be more active on social media, though.

I knew that more content would keep people excited about the project while I was hard at work coding. Maybe an extra 3-4 posts after a new release to help promote it for a longer period of time. It would give folks a second (or third) chance to see that I’ve released a new version, in case they missed the original announcement. It would also create more opportunities for people to retweet/share some posts and attract the attention of more potential new users.

Time commitment is something to keep in mind, though. I’d love to write up a ton of really in-depth blog posts and record some educational screencasts for my projects. Those are both certainly good resources to have available for my users, but they’re also just as much work as writing code. I don’t think I can do three or four of those between releases without committing to several days of work. Ideally, I could generate enough interesting social media content for a new release in a day or less.

Recently, I came up with something so simple that it probably should have been obvious in hindsight. When I’m putting the finishing touches on a new version of a project, I pull out some screen recording software and create 3-5 animated GIFs of the new features in action. They don’t need to be longer than several seconds, so they’re super easy to make. In fact, sometimes only a screenshot is all that’s needed. I can make these GIFs all at once, and then post one every week for a few weeks following a release.

When I first thought of using these animated GIFs, I went back and forth in my head whether it was a good idea or not. I personally get annoyed when a product’s social media account shares the exact same content, like a blog post, more than once. I follow about 75 people/products on Twitter, and that means I can read every single tweet in my timeline. Basically, I force myself to create a feed that is always filled with interesting content. If an account tweets too often, or if they duplicate the same links over and over again, I’m wasting my time on too much noise, and they get unfollowed. That’s my metric for figuring out how my project’s social media accounts should “behave”.

It came down to this: The original announcement of my project’s new version is basically a list of cool, new features all mashed together into 140 characters or less. I might include one image to highlight something particularly cool, but I can’t cover everything in detail using one tweet. The animated GIFs make good followup posts because they can focus on showing off one, individual feature in more detail. That adds value beyond the original post, which is exactly what I want.

What other easy ways are there to promote a project on social media without a big time commitment?

  • In the past, I’ve shared previews of new features when they’re still in development and not quite finished yet. That can be exciting for users, but it can also take away some of the impact when those features are finally released at a later date.
  • It’s always good to see a cool app that someone built with one of my open source libraries. That’s an obvious thing to share with my followers, of course. However, most developers don’t go out of the way to show me their projects that use my code, and if they did the work for an employer, they might not be allowed to share. That means I can’t necessarily make this type of post very consistently.

Those are a couple of types of content that I like to share when I can, but do you have any other ideas?