Accessing FrintJS App Providers inside React.js Components

Fahad Heylaal
FrintJS
Published in
5 min readNov 28, 2017

Previously, we covered creating Apps with providers, and then rendering them with React.js in the browser and server. Today, we will see how we can access the providers defined in FrintJS Apps from inside React components.

Higher-order component

We ship a higher-order component (HoC) called observe from our React integration package frint-react.

The observe HoC allows you to wrap your target Component while giving you access to your FrintJS App’s instance, enabling you to prepare the props that you can pass to your target Component later.

You can either generate a synchronous props object, or stream it asynchronously using an Observable with this HoC.

Example App

For the examples below, we will assume this is how our App is defined and rendered with React:

import { createApp } from 'frint';
import { render } from 'frint-react';
import MyComponent from './MyComponent';const App = createApp({
name: 'MyApp',
providers: [
{
name: 'foo',
useValue: 'foo value here',
},
{
name: 'component',
useValue: MyComponent,
},
],
});
const app = new App();
render(app, document.getElementById('root'));

Synchronous props

If you already know what props you want to pass to your target Component, and don’t need any asynchronous operations involved, and the props are not expected to change later, this solution is suitable for you.

Let’s assume you have a Component like this:

function MyComponent(props) {
const { appName } = props;
return <p>App name: {appName}</p>;
}
export default MyComponent;

The component expects an appName prop, which we expect to be our FrintJS App’s name “MyApp”.

But how can we pass this as a prop? How can we even access the App’s instance?

We do it with our observe HoC:

import React from 'react';
import { observe } from 'frint-react';
function MyComponent(props) {
const { appName } = props;
return <p>App name: {appName}</p>;
}
export default observe(function (app) {
return {
appName: app.getName(),
};
})(MyComponent);

The observe HoC receives a function, where you can access the App’s instance. Since you have access to App, you can get its name, and then return a props-compatible object, that your target component will receive when rendering.

You could also get a provider’s value, process it further, and then return the object. For e.g., we could also additionally pass a foo prop:

export default observe(function (app) {
return {
appName: app.getName(),
foo: app.get('foo'), // `foo value here`
};
})(MyComponent);

Now your target component will receive both appName and foo as props.

Streaming props

I wrote another article some time ago on how to stream props with RxJS in React components. I highly recommend you to read that too.

You may wonder by now, if the observe higher-order component is used for generating props synchronously, why is it called “observe” then?

That’s because observe HoC previously only supported returning an Observable, and returning plain objects was supported much later.

Imagine, you have a component like this:

import React from 'react';function MyComponent(props) {
const { interval } = props;
return <p>Interval: {interval}</p>;
}
export default MyComponent;

The component receives an interval prop, that we expect to increment by 1 every second.

We could visualize it in a marble diagram here:

Illustration of props object inside your target component over time

In the first second, the interval prop is 1, then 2 and so on…

The interval logic can be expressed with RxJS in a variable like this:

import { interval } from 'rxjs/observable/interval';const interval$ = interval(1000); // emits every 1 second

Now gluing it all up using our observe HoC is as easy as:

import React from 'react';
import { observe } from 'frint-react';
import { interval } from 'rxjs/observable/interval';
import { map } from 'rxjs/operators/map';
function MyComponent(props) {
const { interval } = props;
return <p>Interval: {interval}</p>;
}
export default observe(function (app) {
const interval$ = interval(1000);
return interval$.pipe(
map(x => ({ interval: x }))
);
})(MyComponent);

What we just did above is prepare an interval$ Observable, and then map it to a props-compatible object, and return the final Observable.

The example above uses lettable operators using pipe method, which is introduced since RxJS v5.5.

The HoC will now take care of updating the props and keep passing it down to your target component.

While this example didn’t use any provider, but you can imagine the interval$ coming from a provider too instead:

export default observe(function (app) {
const interval$ = app.get('interval');
return interval$.pipe(
map(x => ({ interval: x }))
);
})(MyComponent);

Helper function

We also have a helper function called streamProps that we ship from our frint-react package. It is useful if you are combining a stream of props from various sources (involving both sync and async operations).

The above example could have been written like this:

import { streamProps } from 'frint-react';
import { interval } from 'rxjs/observable/interval';
const interval$ = interval(1000);const props$ = streamProps()
// sync
.set('foo', 'foo value')
// observable, with further mappings
.set(
interval$,
x => ({ interval: x })
)
// create a combined Observable of all props together
.get$();

Now when you return this props$ observable from your observe HoC, your target component will receive both foo and interval prop together.

While foo's value will always remain the same, interval will keep updating every second.

Benefits of this approach

Using the observe higher-order component encourages you to keep your React.js components as stateless as possible. This way, when you write your main components, you only need to worry about what props you receive, and nothing else.

And as long as your FrintJS App has something defined as a provider, you are allowed to access it from your components layer anywhere.

This approach also helps in moving a larger share of stateful logic to provider-level in FrintJS Apps, leaving your components responsible for rendering and passing data as props only.

--

--

Fahad Heylaal
FrintJS

Maker of things · JavaScript · RxJS · ReactJS