Connect
The data-fetching decorator for Lore
The data-fetching decorator for Lore
This is a component that emulates the behavior of connect
, but is intended to be used as a component within the render function, and not as a decorator.
Example usage looks like this:
import { Connect } from 'lore-hook-connect';
export default createReactClass({
render() {
return (
<Connect callbacks={(getState, props) => {
return {
tweets: getState('tweet.find')
}
}}>
{(props) => {
const { tweets } = props;
return (
<ul>
{tweets.data.map(function(tweet) {
return (
<li>{tweet.data.text}</li>
);
})}
</ul>
);
}}
</Connect>
);
}
})
The primary motivation for creating this component grew from a repeated need to render many-to-many data.
An example to illustrate:
Let's say we have an API with the following data:
{
tweets: [
{
id: 1,
user: 1,
text: '@lore is a React framework'
}
],
users: [
{
id: 1,
username: 'jchansen'
},
{
id: 2,
username: 'lore'
}
],
mentions: [
{
id: 1,
tweet: 1,
user: 2
}
]
}
This data describes two users
on Fake Twitter (jchansen
and lore
) and one tweet
by jchansen
where he mentions @lore
by saying @lore is a React framework
.
In this example, we want to render all tweets where @lore
is mentioned, using the component below.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
export default createReactClass({
displayName: 'Tweet',
propTypes: {
tweet: PropTypes.object.isRequired,
},
render() {
const { tweet } = this.props;
return (
<li>{tweet.data.text}</li>
);
}
}))
If this example, if we use only the connect
decorator, we will need two components, mocked out below.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import Mention from './Mention';
export default connect((getState, props) => {
return {
mentions: getState('mention.find', {
where: {
user: 2
}
})
}
})(
createReactClass({
displayName: 'Mentions',
propTypes: {
mentions: PropTypes.object.isRequired
},
render() {
const { mentions } = this.props;
return (
<ul>
{mentions.data.map((mention) => {
return (
<Mention
key={mention.id}
mention={mention}
/>
)
})}
</ul>
);
}
}))
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect } from 'lore-hook-connect';
import Tweet from './Tweet';
export default connect((getState, props) => {
const { mention } = props;
return {
tweet: getState('tweet.byId', {
id: mention.data.tweet
})
}
})(
createReactClass({
displayName: 'Mention',
propTypes: {
mention: PropTypes.object.isRequired,
tweet: PropTypes.object.isRequired,
},
render() {
const { tweet } = this.props;
return (
<Tweet tweet={tweet} />
);
}
}))
The problem (or at least the motivation for Connect
) comes from the fact that the Mention
component is really just transforming a mention
into a tweet
. In other words, we created a component just to transform data.
As a one-off, it's not too annoying, but in an application with a lot of many-to-many endpoints like mentions
, it can start to feel like a lot of tedious boilerplate, as a great many of these "transform" components are required.
To illustrate the value of Connect
, let's do that same example again, but this time we'll do it one component.
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { connect, Connect } from 'lore-hook-connect';
import Tweet from './Tweet';
export default connect((getState, props) => {
return {
mentions: getState('mention.find', {
where: {
user: 2
}
})
}
})(
createReactClass({
displayName: 'Mentions',
propTypes: {
mentions: PropTypes.object.isRequired
},
render() {
const { mentions } = this.props;
return (
<ul>
{mentions.data.map((mention) => {
return (
<Connect key={mention.id} callback={(getState) => {
return {
tweet: getState('tweet.byId', {
id: mention.data.tweet
})
}
}}>
{(props) => {
const { tweet } = props;
return (
<Tweet tweet={tweet} />
);
}}
</Connect>
);
})}
</ul>
);
}
}))
In this example, it's just simpler. We create a component called Mentions
intended to render each Tweet
that mentions @lore
(the user with the id of 2). And using the Connect
component, we're able to do whatever transformations we need inside the render
method, and return a Tweet
when we're ready.
Now if we no longer need to render mentions
, we can delete just this one file, instead of two, and the logic is (arguable) easier to understand as well, since we don't need to jump through multiple components to understand how a mention
became a tweet
.