Deep dive into observed Components with React.js and FrintJS
Our focus in this article will be about using the observe
higher-order component, shipped from frint-react
package.
Even though FrintJS is rendering library agnostic (and supports React and Vue.js both), we will stick to React.js only for the sake of this article.
To make the most out of this, it is advised that you read these previous articles from our blog first:
Higher-order component
The API of the observe
higher-order component (HoC) is quite simple:
import React from 'react';
import { observe } from 'frint-react';function MyComponent(props) {
return <div>...</div>;
}const ObservedComponent = observe(fn)(MyComponent);export default ObservedComponent;
It receives a function (that we called fn
above), where you can generate your props that will be ultimately passed to your target component, and returns a function that accepts the component you want to observe (in this case MyComponent
).
Generating props synchronously
The fn
function also gives you access to the FrintJS App’s instance:
const ObservedComponent = observe(function (app) {
// this will be the `props` in MyComponent
return {};
})(MyComponent);
Since you have access to your app
instance, you can also get values from it, including providers:
const ObservedComponent = observe(function (app) {
return {
appName: app.getName(),
foo: app.get('foo'),
};
})(MyComponent);
Now your MyComponent
will receive both appName
and foo
as props.
Props as an Observable
The observe
HoC is powerful enough to return props as a stream expressed with an RxJS Observable too.
Think of an interval, that changes over time:
import { interval } from 'rxjs/observable/interval';const interval$ = interval(1000); // emits every 1 second
We can map it further, to convert it to a props-compatible object that our MyComponent
can understand:
import { interval } from 'rxjs/observable/interval';
import { map } from 'rxjs/operators/map';const interval$ = interval(1000);const props$ = interval$.pipe(
map(x => ({ interval: x }))
);
We just created a new props$
observable, that is mapping interval$
into emitting an object with this structure: { interval: 1 }
.
Now we can connect it to our MyComponent
using the observe
HoC:
import { interval } from 'rxjs/observable/interval';
import { map } from 'rxjs/operators/map';const ObservedComponent = observe(function (app) {
const interval$ = interval(1000); return interval$.pipe(
map(x => ({ interval: x }))
);
})(MyComponent);
When this component is rendered, MyComponent
will be receiving the prop interval
that will keep incrementing every second triggering a re-render.
Accessing parent component’s props
Before returning the props Observable, it is possible that you may need to access the props passed from a parent component (if any).
In React, props passed down from parent components can change any time. Because they have this dynamic nature, the observe
HoC gives you access to parent props as an Observable:
const ObservedComponent = observe(function (app, props$) {
// ...
})(MyComponent);
In addition to your FrintJS App instance (app
), there is a second argument props$
, which is props passed down to you from the parent Component expressed as an Observable.
Using helper function for generating props stream
The examples above were pretty straight forward, and worked with only a single Observable. But as your application grows, there will be times, when you need to work with multiple Observables and return a single props stream.
This is where a helper function called streamProps
shipped with frint-react
can come handy.
If you are an RxJS ninja, you may skip this part =D
The streamProps
function will allow you to keep setting values (with chaining) until you are done and then generate a single Observable out of all the set values.
Code example:
import { observe, streamProps } from 'frint-react';const ObservedComponent = observe(function (app, props$) {
return streamProps()
// synchronous values
.set('foo', 'foo value')
.set({ bar: 'bar value' }) // values from Observables
.set(
interval$, // rest of the arguments are mappers
x => ({ interval: x })
)
.set(
props$,
parentProps => parentProps.somePropName,
somePropName => ({ baz: somePropName })
) // generate a single Observable of props
.get$();
})(MyComponent);
The code above is generating a props stream consisting of four props together:
foo
: with valuefoo value
bar
: with valuebar value
interval
: with an integer value that keeps incrementingbaz
: which has a value coming from a prop (somePropName
) passed from parent component
All these four props are then made available to MyComponent
.
Starting with default props
Depending on the asynchronous nature of Observables, some may or may not fire right away. In that case, you may want to pass some default props to your target component before new values are generated.
The streamProps
helper function receives an optional first argument, where you can pass your default props:
import { streamProps } from 'frint-react';const defaultProps = {
foo: 'n/a',
bar: 'n/a',
baz: 'n/a',
};const props$ = streamProps(defaultProps)
.set(...)
.get$();
It will then start the props stream with n/a
as initial values, and as soon as values for these props are available, they will be updated and streamed to your target component.
Summary
To summarize, we can say that the observe
HoC does the following:
- Wraps your target component, returning a new observed component
- Gives access to FrintJS App instance
- Gives access to props passed from parent component as an Observable
- Allows you to generate props for your target component either synchronously or expressed with an Observable
This approach helps and encourages you write your React components in a stateless way as much as possible, leaving it free of any logic and deal with props only.
Find us on Twitter if you have any feedback for us!