react-render-props-script-loader

an easier to use dynamic script loader

Downloads in past

Stats

StarsIssuesVersionUpdatedCreatedSize
react-render-props-script-loader
301.4.225 days ago6 years agoMinified + gzip package size for react-render-props-script-loader in KB

Readme

react-render-props-script-loader
CircleCI Coverage Status Commitizen friendly npm version
an easier to use dynamic script loader with a render prop and now a React custom hook.
This is useful if you want to wait to load the Google Maps API until the user navigates to a view that uses it. When you mount a <ScriptLoader> component, it will create the script tag you've requested.
<ScriptLoader> doesn't load a given script URL more than once, even if there is a pre-existing <script> tag for that URL that it didn't create. If src prop changes, it will load that new URL.
Version notes
  • if building for legacy browsers with a bundler like Webpack that supports the
module field of package.json, you will probably need to add a rule to transpile this package.
Installation
npm install --save react-render-props-script-loader
Example
import * as React from 'react'
import ScriptLoader from 'react-render-props-script-loader'

import MapView from './MapView'

export const MapViewContainer = (props) => (
  <ScriptLoader
    type="text/javascript"
    src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places"
    onLoad={() => console.log('loaded google maps!')}
    onError={(error) =>
      console.error('failed to load google maps:', error.stack)
    }
  >
    {({ loading, error }) => {
      if (loading) return <h3>Loading Google Maps API...</h3>
      if (error) return <h3>Failed to load Google Maps API: {error.message}</h3>
      return <MapView {...props} />
    }}
  </ScriptLoader>
)
API

useScript(props: Props)

import { useScript } from 'react-render-props-script-loader'

props.src (required string)

The script URL.

props.onLoad (?() => any)

A callback that ScriptLoader will call once the script has been loaded

props.onError (?(error: Error) => any)

A callback that ScriptLoader will call if there was an error loading the script

Returns

A state object of the following type:
type State = {
  loading: boolean
  loaded: boolean
  error: ?Error
  promise: ?Promise<any>
}

ScriptLoader

import ScriptLoader from 'react-render-props-script-loader'

src (required string)

The script URL.

onLoad (?() => any)

A callback that ScriptLoader will call once the script has been loaded

onError (?(error: Error) => any)

A callback that ScriptLoader will call if there was an error loading the script

children (?(state: State) => ?React.Node)

The render function. It will be called with an object having the following props, and may return your desired content to display:
{
  loading: boolean,
  loaded: boolean,
  error: ?Error,
  promise: ?Promise<any>,
}

Server-Side Rendering

import {
  ScriptsRegistry,
  ScriptsRegistryContext,
} from 'react-render-props-script-loader'

On the server, create an instance of ScriptsRegistry and put it on the app's context:
const registry = new ScriptsRegistry()

const body = ReactDOM.renderToString(
  <ScriptsRegistryContext.Provider value={registry}>
    <App />
  </ScriptsRegistryContext.Provider>
)

Then render registry.scriptTags() in your head element:
const html = (
  <html className="default">
    <head>
      ...
      {registry.scriptTags()}
    </head>
    ...
  </html>
)

Content Security Policy

Make sure your header includes this meta tag:
<meta property="csp-nonce" content={nonce} />

And in SSR, pass the nonce to registry.scriptTags({ nonce }).