next-mdx-remote
Version:
utilities for loading mdx from any remote source as data, rather than as a local import
74 lines (63 loc) • 2.84 kB
JavaScript
// *** NOTE: Do not use any ES6 features because of IE11 compatibility! ***
require('./idle-callback-polyfill')
var React = require('react')
var MDX = require('@mdx-js/react')
module.exports = function hydrate(params, options) {
var compiledSource = params.compiledSource
var renderedOutput = params.renderedOutput
var scope = params.scope || {}
var components = options && options.components || {}
// our default result is the server-rendered output
// we get this in front of users as quickly as possible
var useStateResult = React.useState(
React.createElement('div', {
dangerouslySetInnerHTML: {
__html: renderedOutput,
},
})
)
var result = useStateResult[0]
var setResult = useStateResult[1]
// if we're server-side, we can return the raw output early
if (typeof window === 'undefined') return result
// if we're on the client side, we hydrate the mdx content inside
// requestIdleCallback, since we can be fairly confident that
// markdown - embedded components are not a high priority to get
// to interactive compared to...anything else on the page.
//
// once the hydration is complete, we update the state/memo value and
// react re-renders for us
React.useEffect(function () {
var handle = window.requestIdleCallback(function () {
// first we set up the scope which has to include the mdx custom
// create element function as well as any components we're using
var fullScope = Object.assign({ mdx: MDX.mdx }, components, scope);
var keys = Object.keys(fullScope)
var values = Object.values(fullScope)
// now we eval the source code using a function constructor
// in order for this to work we need to have React, the mdx createElement,
// and all our components in scope for the function, which is the case here
// we pass the names (via keys) in as the function's args, and execute the
// function with the actual values.
var hydrateFn = Reflect.construct(
Function,
[ 'React' ]
.concat(keys)
.concat(compiledSource + "\nreturn React.createElement(MDXContent, {});")
)
var hydrated = hydrateFn.apply(hydrateFn, [ React ].concat(values))
// wrapping the content with MDXProvider will allow us to customize the standard
// markdown components (such as "h1" or "a") with the "components" object
var wrappedWithMdxProvider = React.createElement(
MDX.MDXProvider,
{ components: components },
hydrated
)
// finally, set the the output as the new result so that react will re-render for us
// and cancel the idle callback since we don't need it anymore
setResult(wrappedWithMdxProvider)
window.cancelIdleCallback(handle)
})
}, [compiledSource])
return result
}