Giter VIP home page Giter VIP logo

reactscriptloader's Introduction

#What is it?

ReactScriptLoader simplifies creating React components whose rendering depends on dynamically loaded scripts. It can be used for lazily loading heavy scripts but it's especially useful for loading components that rely on 3rd party scripts, such as Google Maps or Stripe Checkout.

#Why use it?

React apps are typically single-page apps that are rendered client-side in Javascript. When loading a site built with React, the browser typically pre-loads the javascript necessary to render the site's React components so that they can be rendered with no latency. This works well for sites that serve a relatively small amount of javascript from their own servers in a single bundle. However, in some situations pre-loading all the scripts necessary to render the site's components is impractial. For example, a site may have a Map component that relies on a dynamically loaded 3rd party library to render itself. It may be possible to delay rendering the app until the third party library is finished loading but doing so would make the site feel unnecessarily sluggish. It's a much better strategy to first render the page with a placeholder for the map and asynchronously render the map once the third party library has loaded. Deferring the loading of the external script is even more important when the map component isn't rendered right away but is only revealed after user interaction.

#How does it work?

ReactScriptLoader provides a React mixin that you can add to any component class. In addition to using the mixin, your class should provide a few methods that tell ReactScriptLoaderMixin the script's URL and how to handle script load and error events. ReactScriptLoaderMixin handles loading the scripts, notifying the components that depend on a script after they mount and cleaning before the components unmount. ReactScriptLoader ensures that a script is only loaded once no matter how many components use it.

Examples

Basic Example

Here's the most basic example for implementing a React class that uses ReactScriptLoaderMixin. It uses require-js to import modules.

/** @jsx React.DOM */

var React = require('react');
var ReactScriptLoaderMixin = require('react-script-loader').ReactScriptLoaderMixin;

var Foo = React.createClass({
	mixins: [ReactScriptLoaderMixin],
	getInitialState: function() {
		return {
			scriptLoading: true,
			scriptLoadError: false,
		};
	},

	// this function tells ReactScriptLoaderMixin where to load the script from
	getScriptURL: function() {
		return 'http://d3js.org/d3.v3.min.js';
	},

	// ReactScriptLoaderMixin calls this function when the script has loaded
	// successfully.
	onScriptLoaded: function() {
		this.setState({scriptLoading: false});
	},

	// ReactScriptLoaderMixin calls this function when the script has failed to load.
	onScriptError: function() {
		this.setState({scriptLoading: false, scriptLoadError: true});
	},

	render: function() {
		var message;
		if (this.state.scriptLoading) {
			message = 'loading script...';
		} else if (this.state.scriptLoadError) {
			message = 'loading failed';
		} else {
			message = 'loading succeeded';
		}
		return <span>{message}</span>;
	}
});

A Google Maps component

You may want to do some additional initialization after the script loads and before ReactScriptLoaderMixin calls onScriptLoaded. For example, the Google maps API calls a JSONP callback on your page before you can start using the API. If you naively try calling the Google maps methods in onScriptLoaded you'll probably see 'undefined is not a function' errors in the javascript console. ReactScriptLoader helps you avoid this problem by implementing the deferOnScriptLoaded() callback in your component class. If this method returns true, ReactScriptLoaderMixin will wait on calling onScriptLoaded() until you manually call ReactScriptLoader.triggerOnScriptLoaded(scriptURL) method. This is best illustrated in the following example:

/** @jsx React.DOM */

var React = require('react.js');

var ReactScriptLoaderModule = require('react-script-loader');
var ReactScriptLoaderMixin= ReactScriptLoaderModule.ReactScriptLoaderMixin;
var ReactScriptLoader= ReactScriptLoaderModule.ReactScriptLoader;

var scriptURL = 'https://maps.googleapis.com/maps/api/js?v=3.exp&callback=initializeMaps';

// This function is called by the Google maps API after its initialization is
// complete.
// We need to define this function in the window scope to make it accessible
// to the Google maps script.
window.initializeMaps = function() {

	// This triggers the onScriptLoaded method call on all mounted Map components.
	ReactScriptLoader.triggerOnScriptLoaded(scriptURL);
}

var Map = React.createClass({
	mixins: [ReactScriptLoaderMixin],
	getScriptURL: function() {
		return scriptURL;
	},

	// Ensure that onScriptLoaded is deferred until the
	// ReactScriptLoader.triggerOnScriptLoaded() call above is made in
	// initializeMaps().
	deferOnScriptLoaded: function() {
		return true;
	},

	onScriptLoaded: function() {
		// Render a map with the center point given by the component's lat and lng
		// properties.
		var center = new google.maps.LatLng(this.props.lat, this.props.lng);
	  	var mapOptions = {
		    zoom: 12,
		    center: center,
		    disableDefaultUI: true,
		    draggable: false,
		    zoomControl: false,
		    scrollwheel: false,
		    disableDoubleClickZoom: true,
		  };
  		var map = new google.maps.Map(this.getDOMNode(), mapOptions);
	},
	onScriptError: function() {
		// Show the user an error message.
	},
	render: function() {
		return <div className="mapCanvas"/>;
	},

});

exports.Map = Map;

A Stripe Checkout example

This last example shows how to create a component called StripeButton that renders a button and uses Stripe Checkout to pop up a payment dialog when the user clicks on it. The button is rendered immediately but if the user clicks before the script is loaded the user sees a loading indicator, which disappears when the script has loaded. (Additional logic should be added to remove the loading dialog once all StripeButtons have been unmounted from the page. This remains an exercise for the reader :) ) If the script fails to load, we show the user an error message when the user clicks on the button.

/** @jsx React.DOM */

var React = require('react');
var ReactScriptLoaderMixin = require('react-script-loader').ReactScriptLoaderMixin;

var StripeButton = React.createClass({
	mixins: [ReactScriptLoaderMixin],
	getScriptURL: function() {
		return 'https://checkout.stripe.com/checkout.js';
	},

	statics: {
		stripeHandler: null,
		scriptDidError: false,
	},

	// Indicates if the user has clicked on the button before the
	// the script has loaded.
	hasPendingClick: false,

	onScriptLoaded: function() {
		// Initialize the Stripe handler on the first onScriptLoaded call.
		// This handler is shared by all StripeButtons on the page.
		if (!StripeButton.stripeHandler) {
			StripeButton.stripeHandler = StripeCheckout.configure({
				key: 'YOUR_STRIPE_KEY',
  				image: '/YOUR_LOGO_IMAGE.png',
  				token: function(token) {
    					// Use the token to create the charge with a server-side script.
		  		}
		  	});
  			if (this.hasPendingClick) {
				this.showStripeDialog();
			}
		}
	},
	showLoadingDialog: function() {
		// show a loading dialog
	},
	hideLoadingDialog: function() {
		// hide the loading dialog
	},
	showStripeDialog: function() {
		this.hideLoadingDialog();
		StripeButton.stripeHandler.open({
      			name: 'Demo Site',
      			description: '2 widgets ($20.00)',
      			amount: 2000
    		});
	},
	onScriptError: function() {
		this.hideLoadingDialog();
		StripeButton.scriptDidError = true;
	},
	onClick: function() {
		if (StripeButton.scriptDidError) {
			console.log('failed to load script');
		} else if (StripeButton.stripeHandler) {
			this.showStripeDialog();
 		} else {
 			this.showLoadingDialog();
 			this.hasPendingClick = true;
 		}
	},
	render: function() {
		return (
			<button onClick={this.onClick}>Place order</button>
		);
	}
});

exports.StripeButton = StripeButton;

reactscriptloader's People

Contributors

christopherstyles avatar chuckreynolds avatar lmb avatar marciovicente avatar mtchavez avatar pc avatar pkese avatar sinceow avatar spirinvladimir avatar yariv avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.