Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing embeddable Javascript plugin with React & Webpack

I want to be able to bundle my React app with Webpack such that distributed copies put onto a CDN can be sourced, called and initialised with a bunch of config relevant to a client.

After reading this and this, I'm setting up my webpack entry file as follows:

// ... React requires etc.  (() => {   this.MyApp = (config) => {     // some constructor code here   }    MyApp.prototype.init = () => {     ReactDOM.render(<MyReactApp config={MyApp.config} />, someSelector);   } })(); 

The idea being that in my client, I can do something like the following:

<script src="./bundle.js" type="text/javascript"></script> <script type="text/javascript">   MyApp.init({     some: "config"   }); </script> 

And my MyApp#init function will render my React app inside some container on the client.

Am I thinking about this in the right way? Is there a simpler or more efficient way to go about this?

My error is Uncaught TypeError: Cannot set property 'MyApp' of undefined, since this inside the IIFE is undefined. I'd really like to understand both why this is happening and advice on how to fix it.

Thanks in advance!

like image 900
mattsch Avatar asked May 15 '16 14:05

mattsch


People also ask

Is it OK to use jQuery with react?

The main limitation of using jQuery in React is that jQuery manually updates the DOM. On the other hand, React has its system for making changes to the DOM. It internally determines when a React app or component should re-render. It also figures out which parts of the UI need to be updated.


1 Answers

So I kind of found a solution to this, as described here

If I change my webpack.config.js file to add the following attributes to the output object, i.e.

var config = {   // ...   output: {     // ...     library: 'MyApp',     libraryTarget: 'umd',     umdNamedDefine: true,   } } 

This specifies the file I'm bundling with webpack as a UMD module, so if I have a function in that file and export it...

export const init = (config) => {   ReactDOM.render(<MyReactApp config={config} />, someSelector); } 

I can then, in my client, do the following.

<script src="./bundle.js" type="text/javascript"></script> <script type="text/javascript">   MyApp.init({     some: "config"   });  </script> 

And my React app renders.

If anyone thinks this is a daft way of doing it, I'd love to hear it!

MORE INFORMATION ON WEBPACK CONFIG

Please bear in mind I haven't touched this code in a while. Given it's Javascript, the world has likely moved on and some practises may be outdated.

This is my React entrypoint file (src/index.js)

import 'babel-polyfill'; import React from 'react'; import { render } from 'react-dom'; import Root from './components/Root'; import configureStore from './lib/configureStore';  const store = configureStore();  export const init = (config) => {   render(     <Root store={store} config={config} />,      document.querySelector(config.selector || "")   ); } 

This is my Webpack config (webpack.config.js)

var webpack = require('webpack'); var path = require('path'); var loaders = require('./webpack.loaders');  module.exports = {     entry: [         'webpack-dev-server/client?http://0.0.0.0:8080', // WebpackDevServer host and port         'webpack/hot/only-dev-server',         './src/index.js' // Your appʼs entry point     ],     devtool: process.env.WEBPACK_DEVTOOL || 'source-map',     output: {         path: path.join(__dirname, 'public'),         filename: 'bundle.js',         library: 'Foo',         libraryTarget: 'umd',         umdNamedDefine: true,     },     resolve: {         extensions: ['', '.js', '.jsx']     },     module: {         loaders: loaders     },     devServer: {         contentBase: "./public",             noInfo: true, //  --no-info option             hot: true,             inline: true         },     plugins: [         new webpack.NoErrorsPlugin()     ] }; 

As you can see, my Webpack config outputs my bundle.js which is what my front-end will ingest.

like image 173
mattsch Avatar answered Oct 22 '22 03:10

mattsch