A filter let’s us see things clearly in a noisy world.

Click here to share this article on LinkedIn »

The React library has become very popular among web developers. A useful accessory often used with React is PropTypes which is a way of making runtime assertions about what type of data a React component requires in order to render properly. Runtime assertions such as PropTypes are independent of build time type checking and are useful for testing and debugging and will greatly reduce the maintenance cost of a complex application. In this article, I will start out talking about React PropTypes and will end up talking about making similar but better assertions using the ARCcore Filter library. By the end of the article, you will understand how to use ARCcore.Filter to provide a declarative type check for any JavaScript function in order make your applications more robust and maintainable.

To begin, let’s discuss React PropTypes by way of a simple example. Our example consists of a React Component that accepts an array of data for props. Each object in the array has a ‘name’ and a ‘url’ member and the component renders a list where each item in the list contains a link to the url with the link text of the name. See the example code from code sandbox which is also shown below:

In the example the ‘ImageList’ component validates that it is receiving an array by using a PropTypes declaration;

static propTypes = { renderData: PropTypes.objectOf( PropTypes.arrayOf( PropTypes.shape({ url: PropTypes.string.isRequired, name: PropTypes.string.isRequired }) ))};

With the PropTypes declaration, if the component is passed an argument that is not an array for the ‘renderData’ prop, or if the elements of the array do not contain strings ‘url’ and ‘name’ an error will be logged to the console to warn me that I have violated the data contract for my component.

You can fork the sandbox and try an experiment by changing the prop type on line 6 to:

renderData: PropTypes.objectOf(PropTypes.object)

Reload the page, and look in the JavaScript console and you will see an error like this:

Warning: Failed prop type: Invalid prop `renderData.imageList` of type `array` supplied to `ImageList`, expected `object`. in ImageList (created by App) in App

Since I wanted deep type checking for PropTypes, I needed to use the ‘shape’ function of PropTypes.

PropTypes.shape({ url: PropTypes.string.isRequired, name: PropTypes.string.isRequired })

The ARCcore Filter library provides a better way to specify what an object shape should be through it’s ‘Filter Spec’ mechanism. An example of a Filter Spec that describes the data needed by the ImageList component is shown below:

inputFilterSpec: { ____label: "ImageList filter spec", ____description: "Filter spec for props for the image list view", ____types: "jsObject", //The entity below is an object imageList: { ____types: "jsArray", //The object has an imageList array element: { ____types: "jsObject", //Each array element must be an object url: { ____accept: "jsString" //The object has a string 'url' }, name: { ____accept: "jsString" //The object has a string 'name' } } } }

The Filter Spec is simply a JavaScript object. Fields in the object that begin with a quanderscore “____” have special meaning. The first 2 fields ‘___label’ and ‘____description’ are optional fields for humans to read that describe the Filter Spec. The next field:

____types: “jsObject”

indicates that the thing to be described below it is an object. The object is further specified in that it will contain a member named ‘imageList’ and that each element of that list will contain keys for ‘url’ and ‘name’ which are both strings. The format of the Filter Spec will let us specify an object of arbitrary complexity and by looking at the Filter Spec we can easily see what the shape of that object will be.

Using an object instead of nested functions to specify an input has several advantages. One is that the object is just data, not code, which makes the specification declarative. There is no need to write tests for the object, the code that parses the object (the ARCcore Filter) has it’s own test tests to ensure that inputs are correctly validated based on Filter Specs. Object are easy to read, they can be imported, serialized, sent over networks and shared as needed.

Filters Specs are just objects that don’t do anything by themselves, but are used as part of a function to implement an ARCcore Filter. ARCcore Filter is simply a function wrapper; it has two Filter Specs, one for the input and one for the output. It also has body function which takes input and produces and output. Since we are only interested in type validation for this example, we can create a filter with an empty body function, using our Filter Spec above to do type validation.

Using the above Filter Spec, I can write a simple ARCcore Filter that is called inside a custom validator which I can use for my component PropType. See the code sandbox example here which is also shown below.

Example of a PropType with a custom validator implemented using ARCcore Filter.

Before going any further let’s talk in detail about what an ARCcore Filter is.

An ARCcore Filter is a composite function (a function chain). There are three functions that are called each time a Filter is invoked. One function validates the input based on the inputFilterSpec, another is the bodyFunction provided by the writer of the filter, and the 3rd is a function that validates the output of the body function based on the outputFilterSpec. To create a new Filter you call a factory function ‘arccore.filter.create’ (line 3 in the code above). The factory function is passed an object which includes the input and output Filter Specs, a body function and some other metadata including a unique identifier (line 4) as well as a name and description (lines 4 and 5). Similar to a Filter, the factory function for Filter returns an object with an ‘error’ and ‘result’ member. A Filter always returns an object with ‘result’ and ‘error’ members. If the error member is non-null then the result can be used. The pattern for calling a Filter is to pass data to the Filter through the request method, check to see if there is an error, and if the error is null use the result. The useful work of a Filter is done by implementing, the bodyFunction. In this example, the bodyFunction is trivial because we are only interested in type validation on the input.

Creating a custom validator for PropTypes using an ARCcore Filter saves the work of having to write an actual function for type checking. The Filter Spec itself is just an object, so now my type checking has become declarative and I can specify a type check with any level of complexity.

To see how the type validation works, for the sandbox above. In ‘App.js’ change one of the objects in the array like the one at line 6:

{ name-1: "cat", url: "https://static.pexels.com/photos/20787/pexels-photo.jpg" }

Now one of the objects has a member ‘name-1’ instead of ‘name’. Reloading the render page will produce the following error in the console:

Warning: Failed prop type: Filter [3RR5VT_5TBWuzX_VhMG4dQ::ImageDisplayPropsFilter] failed while normalizing request input. Error at path '~.imageList[0].name': Value of type 'jsUndefined' not in allowed type set [jsString]. in ImageList (created by App) in App

The message tells you what constraint was violated in the data and what Filter the violation is from. The presence of the operationId in the message gives the ability to find the Filter easily even in a large code base.

So now we have deep type validation that can be changed easily by modifying the Filter Spec. Another characteristic of Filter is that it does not pass through anything not in the Filter Spec, in other words it filters out unwanted members as well as validating required ones.

This might be a good time to declare victory and stop writing but there is just one problem. There is a lot of overlap between PropTypes and ARCcore Filter. If you declare a PropType for a React component when React goes to render that component if first calls a function to verify the props, and then it calls the component as a function to actually render. In a similar manner, if you have a Filter, a function is first called to verify the input, and then a function is run on that input that returns a result. A React component with PropTypes and ARCcore Filter are both doing the same thing in that they chain functions together; one function to validate input and another function to actually do something and return a result. We can do better than duplicating functionality in our application, so why not just do everything with Filter and dispense with PropTypes altogether? We can simply create a Filter that uses our Filter Spec from above that has a body function that returns our rendered content using our original component which is imported into the Filter. Now any component that wants to render ImageList, can simply call the Filter we have created (the rendering Filter) instead of calling ImageList directly. By calling the rendering Filter we are guaranteeing that the right type of data is passed, and that any errors are handled. Our application using a rendering Filter can be seen in in a code sandbox here as shown below:

Using an ARCcore Filter for rendering eliminates the need for PropTypes

In ‘App.js’ in the above sandbox, note the pattern for calling a Filter and rendering the result using a ternary operator.

const renderingResponse = ImageListRenderingFilter.request({ imageList: images }); const renderResult = renderingResponse.error ? ( <div>{renderingResponse.error}</div> ) : ( renderingResponse.result ); return <div>{renderingResponse.result}</div>;

We are passing our data to a Filter by invoking a ‘request’ function. The request returns an object with a ‘result’ and and ‘error’ member. We will have a ‘result’ that can be used if the ‘error’ is not null. This pattern zealously detects errors and reports them in a consistent manner. This is similar to the pattern we had while using PropTypes for our component except we are being more explicit about it.

So far, we have talked about PropTypes and ARCcore Filter, below is a comparison between the two.

Use function chaining to validate input before calling a function ?

Both do this.

Way of declaring valid inputs ?

PropTypes: Simple types available, nested functions, or write a custom function.

ARCCore Filter: Declarative using objects

Where Can It Be Used?

PropTypes: props on React Components in test environments.

ARCcore Filter: Anywhere in JavaScript.

Error Handling ?

PropTypes: Logs error to console on dev environment only.

ARCcore Filter: Compels the programmer to explicitly handle errors.

In short, ARCcore Filter is similar to PropTypes except that it is a more general solution and data validation is performed in a declarative manner.

Filter Spec Fun Facts

You can specify multiple allowed types for a single member such as:

____accept: ['jsString', 'jsNull', 'jsUndefined']

You can specify default values for any part of the tree:

____types: 'jsObject',

____defaultValue: {foo: {bar: 'baz'}, {aNumber: 1}},

The above is very handy for generating data sets that meet a specification.

You can specify a set of allowed values:

____inValueSet: ['ready', 'waiting', 'error']

or ranges:

____inRangeInclusive: { begin: 0, end: 100 }

See the Filter spec documentation for more details.

Advantages of Runtime Assertions

The example above is a basic example of what can be done with ARCcore Filter by way of writing a ‘rendering Filter’. ARCcore Filter provides a function wrapper with built-in runtime assertions. Adding runtime assertions to your code adds more work initially but lowers the total cost of ownership of a codebase over the long-term. Consider the following points:

Most of your effort is spent not on the up-front cost of writing code but on maintaining code. Code is much easier to maintain and debug if things are as explicit as possible and that includes explicit error handling and type declarations. The bane of web applications is inconsistency. Many web applications will appear to work with invalid data and will only manifest errors in subtle ways that are difficult to debug. Debugging applications that do not behave consistently is extremely difficult and time consuming. Many of your data dependencies are met through asynchronous requests, where static typing does not provide any safety. The ability to make assertions easily makes testing much easier and only tests will guarantee that you do not have bugs in your code. Runtime assertions in production applications provide a way to handle errors gracefully.

Here are some other things that ARCcore Filter can be used for:

Establishing explicit data contracts between rendering components in a web application and asynchronous API’s. Building shared component libraries with protection against regressions. Composing complex object hierarchies. Building plugin systems with message-based routing.

By now you should have some idea of how ARCcore Filter works and can be used to improve any code base in JavaScript. The examples provided in this article only scratch the surface on what can be done with ARCcore Filter. More information on ARCcore Filter as well as other useful libraries that can be found at encapsule.io.