Skip to content

Instantly share code, notes, and snippets.

@pre63
Forked from busypeoples/README.md
Created October 28, 2020 04:34
Show Gist options
  • Save pre63/c5733b6e5a3049d66e13f7c4f0b068e1 to your computer and use it in GitHub Desktop.
Save pre63/c5733b6e5a3049d66e13f7c4f0b068e1 to your computer and use it in GitHub Desktop.
Loader

Loader

Introduction

Minimal loader with state management capabilities.

Checkout out the demo.

Setup

Make sure to add a .babelrc file:

{
  "presets": ["es2015", "stage-0", "react"],
  "plugins": ["transform-flow-strip-types", "transform-object-rest-spread"]
}

Example

Uses multiple render props. Loader renders the correct view according to the calculated state.

const fakeFetch = () =>
    new Promise((res, rej) => {
		setTimeout(
	 		() =>
				res([
					{ id: 1, name: 'foo' },
					{ id: 2, name: 'bar' },
					{ id: 3, name: 'baz' }
				]),
			1000
		);
	});

const App = () => (
	<Loader
		fetch={fakeFetch}
		renderError={error => (
			<div class="error">Something went wrong: {error.message}</div>
		)}
		renderSuccess={data => <UserItems data={data} />}
		renderNotAsked={() => <div className="start">Not Data Loaded</div>}
		renderLoading={() => <div className="loader">Loading...</div>}
	>
		{(View, loadData) => (
			<div>
				{View}
				<button onClick={loadData}>Load Data</button>
			</div>
		)}
	</Loader>
);
import React from 'react';
import daggy from 'daggy';
// Inspired by:
// "Slaying a UI Antipattern in Fantasyland" @thinkfunctional
// https://medium.com/javascript-inside/slaying-a-ui-antipattern-in-fantasyland-907cbc322d2a
// "Use a Render Prop!"
// https://cdb.reacttraining.com/use-a-render-prop-50de598f11ce @mjackson
// Loader in ReasonML @ryyppy
// https://github.com/ryyppy/reason-lnd/blob/master/frontend/src/loader.re
const RemoteData = daggy.taggedSum('RemoteData', {
NotAsked: [],
Loading: [],
Failure: ['error'],
Success: ['data']
});
class Loader extends React.Component {
state = { items: RemoteData.NotAsked };
loadData = () => {
const { url, fetch } = this.props;
this.setState(state => ({ items: RemoteData.Loading }));
const getItems = fetch();
getItems.then(
data => {
this.setState(state => ({
items: RemoteData.Success(data)
}));
},
error => {
this.setState(state => ({
items: RemoteData.Failure(error)
}));
}
);
};
getView = items => {
const {
renderNotAsked,
renderLoading,
renderError,
renderSuccess
} = this.props;
return items.cata({
NotAsked: () => renderNotAsked(),
Loading: () => renderLoading(),
Failure: error => renderError(error),
Success: data => renderSuccess(data)
});
};
render() {
const { items } = this.state;
const { children } = this.props;
const View = this.getView(items);
return children(View, this.loadData);
}
}
export default Loader;
{
"name": "loader-demo",
"description": "Minimal loader demo with state handling capabilities",
"scripts": {
"start": "babel src -d lib -w",
"build:lib": "babel src -d lib",
"test": "npm run lint && mocha --compilers js:babel-core/register --recursive --colors"
},
"dependencies": {
"daggy": "^1.2.0",
"react": "^16.0.0"
},
"devDependencies": {
"babel": "^6.23.0",
"babel-cli": "^6.24.1",
"babel-core": "^6.25.0",
"babel-loader": "^7.0.0",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-react-jsx": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment