react-data-binder
v1.1.1
Published
Easily bind data into React Components via props.
Downloads
15
Readme
react-data-binder
Simple way to bind data into React Components via props.
TOC
Why?
Normally, when you are rending a React component. This is the usual process you do:
- Start showing a loading screen.
- Fetch data and wait.
- Once data received, render those data
Then if something goes wrong, you need to show an error.
Additionally, if you create a subscription then you need to clean up those subscriptions when the component is dying.
So, you have to do a lot of stuff.
That's what we are going to fix with this project. You simply tell it how to get data and clean up resources. Then it'll do the hard work you. This is a universal project and work with any kind of data source, whether it's GraphQL, Meteor, Redux or Rx.js observable.
Installation
npm i --save react-data-binder
Basic Usage
Let's say we need to build a clock. First let's create a component to show the time.
const Time = ({time}) => (<div>Time is: {time}</div>);
Now let's define how to fetch data for this:
const onPropsChange = (props, onData) => {
const handle = setInterval(() => {
const time = (new Date()).toString();
onData(null, {time});
}, 1000);
const cleanup = () => clearInteval(handle);
return cleanup;
};
On the above function, we get data for every seconds and send it via onData
. Additionally, we return a cleanup function from the function to cleanup it's resources.
Okay. Now it's time to create the clock:
import { bindData } from 'react-data-binder';
const Clock = bindData(onPropsChange)(Time);
That's it. Now render the clock to the DOM.
import ReactDOM from 'react-dom';
ReactDOM.render(<Clock />, document.body);
See this in live: https://jsbin.com/bejiqu/1/edit?js,output
Additional Benefits
Other than main benefits, now it's super easy to test our UI code. We can easily do it via a set of unit tests.
- For that UI, simply test the plain react component. In this case,
Time
(You can use enzyme for that). - Then test
onPropsChange
for different scenarios.
Do this for all of your components. You can test your code pretty easily.
API
You can customize the higher order component created by bindData
in few ways. Let's discuss.
Handling Errors
Rather than showing the data, something you need to deal with error. Here's how to use bindData
for that:
const onPropsChange = (props, onData) => {
// oops some error.
onData(new Error('Oops'));
};
Then error will be rendered to the screen (in the place where component is rendered). You must provide a JavaScript error object.
You can clear it by passing a some data again like this:
const onPropsChange = (props, onData) => {
// oops some error.
onData(new Error('Oops'));
setTimeout(() => {
onData(null, {time: Date.now()});
}, 5000);
};
Detect props changes
Some times can use the props to custom our data fetching logic. Here's how to do it.
const onPropsChange = (props, onData) => {
const handle = setInterval(() => {
const time = (props.timestamp)? Date.now() : (new Date()).toString();
onData(null, {time});
}, 1000);
const cleanup = () => clearInteval(handle);
return cleanup;
};
Here we are asking to make the Clock to display timestamp instead of a the Date string. See:
ReactDOM.render((
<div>
<Clock timestamp={true}/>
<Clock />
</div>
), document.body);
See this in live: https://jsbin.com/nohuqa/edit?js,output
Change the Loading Component
const MyLoading = () => (<div>Hmm...</div>);
const Clock = bindData(onPropsChange)(Time, MyLoading);
Change the Error Component
const MyError = ({error}) => (<div>Error: {error.message}</div>);
const Clock = bindData(onPropsChange)(Time, null, MyError);
Using with XXX
You can use react-bind-data
with a huge range of projects. Let me show you couple of them:
Bind GraphQL queries via Lokka
const Time = ({time}) => (<div>Time is: {time}</div>);
const client = new Lokka({...});
const query = `
{
time: serverTime
}
`;
const onPropsChange = (props, onData) => {
return client.watchQuery(query);
};
const Clock = bindData(onPropsChange)(Time);
Check here to it in action: https://goo.gl/K1LhYk/
Using with Meteor
For that you need to use bindTrackerData
method instead of bindData
. Then you can watch any Reactive data inside that.
const Time = ({time}) => (<div>Time is: {time}</div>);
const onPropsChange = (props, onData) => {
const handler = Meteor.subscribe('serverTime');
if(hander.ready()) {
const {time} = ServerTimeCollection.findOne();
onData(null, {time});
} else {
onData(null, null);
}
};
// Note the use of bindTrackerData
const Clock = bindTrackerData(onPropsChange)(Time);
Using with Rx.js Observables
var source = Rx.Observable.interval(1000);
const Time = ({time}) => (<div><b>Time is</b>: {time}</div>);
const onPropsChange = (props, onData) => {
const sub = source.subscribe(() => {
const time = new Date().toString();
onData(null, {time})
});
return sub.completed.bind(sub);
};
const Clock = bindData(onPropsChange)(Time);
Try this live: https://jsbin.com/bofaxi/edit?js,output
Using with Redux
const defaultState = {time: new Date().toString()};
const store = Redux.createStore((state = defaultState, action) => {
switch(action.type) {
case 'UPDATE_TIME':
return {
...state,
time: action.time
};
default:
return state;
}
});
setInterval(() => {
store.dispatch({
type: 'UPDATE_TIME',
time: new Date().toString()
});
}, 1000);
const Time = ({time}) => (<div><b>Time is</b>: {time}</div>);
const onPropsChange = (props, onData) => {
return store.subscribe(() => {
const {time} = store.getState();
onData(null, {time})
});
};
const Clock = bindData(onPropsChange)(Time);
ReactDOM.render(<Clock />, document.getElementById('react'))
Try this live: https://jsbin.com/xazuzo/2/edit?html,js,output
Caveats
- In the server, we won't be able to cleanup resources even if you return the cleanup function. That's because, there is no functionality called unmount in the server. So, make sure to handle the cleanup logic by yourself in the server.