Problem with socket.io in react app

Hi, I have a problem with getting react ui to work with uibuilder. At first I tried to build my own react app and then add uibuilders functionality to connect to node-red but when I tried to use uibuilder.start() function it threw an error "io is not a function". Then i tried to run the function with params as suggested in documentation but it didnt work as well. I decided to try to run uibuilder-react-example from github but I got the same results.

Here are steps I took:

-installed node-red
-installed node-red-contrib-uibuilder via node-red palette
-installed reactjs in uibuilder's front end lbraries manager
-pulled uibuilder-react-example from GitHub - gaillarddamien/uibuilder-react-example
-installed app dependencies - npm install
-tried to npm run build it but it threw an error "Cannot find module: 'socket.io-client'. Make sure this package is installed. You can install this package by running: npm install socket.io-client."
-installed socket.io-client
-built app
-created uibuilder node in node-red flow, renamed it to uibuilder-react-example, adjusted advanced settings to run ui from dist folder because it didn't work automatickly nor after reseting and redeploing flow

Error in console in app opened through node-red:

TypeError: r is not a function
    at Object.e.ioSetup (uibuilderfe.js:315)
    at Object.start (uibuilderfe.js:1257)
    at new s (UserData.js:17)
    at vi (react-dom.production.min.js:135)
    at Va (react-dom.production.min.js:181)
    at qu (react-dom.production.min.js:269)
    at Ol (react-dom.production.min.js:250)
    at Cl (react-dom.production.min.js:250)
    at _l (react-dom.production.min.js:250)
    at gl (react-dom.production.min.js:243)
uu @ react-dom.production.min.js:216

react-dom.production.min.js:260 Uncaught TypeError: r is not a function
    at Object.e.ioSetup (uibuilderfe.js:315)
    at Object.start (uibuilderfe.js:1257)
    at new s (UserData.js:17)
    at vi (react-dom.production.min.js:135)
    at Va (react-dom.production.min.js:181)
    at qu (react-dom.production.min.js:269)
    at Ol (react-dom.production.min.js:250)
    at Cl (react-dom.production.min.js:250)
    at _l (react-dom.production.min.js:250)
    at gl (react-dom.production.min.js:243)

Error in console in app opened through reacts developement server:

Uncaught TypeError: io is not a function
    at Object.self.ioSetup (:3000/static/js/vendors~main.chunk.js:344)
    at Object.start (:3000/static/js/vendors~main.chunk.js:1208)
    at new UserData (:3000/static/js/main.chunk.js:625)
    at constructClassInstance (:3000/static/js/vendors~main.chunk.js:20992)
    at updateClassComponent (:3000/static/js/vendors~main.chunk.js:25617)
    at beginWork (:3000/static/js/vendors~main.chunk.js:27212)
    at HTMLUnknownElement.callCallback (:3000/static/js/vendors~main.chunk.js:12177)
    at Object.invokeGuardedCallbackDev (:3000/static/js/vendors~main.chunk.js:12226)
    at invokeGuardedCallback (:3000/static/js/vendors~main.chunk.js:12286)
    at beginWork$1 (:3000/static/js/vendors~main.chunk.js:32028)
    at performUnitOfWork (:3000/static/js/vendors~main.chunk.js:30864)
    at workLoopSync (:3000/static/js/vendors~main.chunk.js:30801)
    at renderRootSync (:3000/static/js/vendors~main.chunk.js:30767)
    at performSyncWorkOnRoot (:3000/static/js/vendors~main.chunk.js:30384)
    at scheduleUpdateOnFiber (:3000/static/js/vendors~main.chunk.js:29972)
    at updateContainer (:3000/static/js/vendors~main.chunk.js:33509)
    at :3000/static/js/vendors~main.chunk.js:34041
    at unbatchedUpdates (:3000/static/js/vendors~main.chunk.js:30524)
    at legacyRenderSubtreeIntoContainer (:3000/static/js/vendors~main.chunk.js:34040)
    at Object.render (:3000/static/js/vendors~main.chunk.js:34123)
    at Module.<anonymous> (:3000/static/js/main.chunk.js:387)
    at Module../src/index.js (:3000/static/js/main.chunk.js:476)
    at __webpack_require__ (:3000/static/js/bundle.js:857)
    at fn (:3000/static/js/bundle.js:151)
    at Object.1 (:3000/static/js/main.chunk.js:957)
    at __webpack_require__ (:3000/static/js/bundle.js:857)
    at checkDeferredModules (:3000/static/js/bundle.js:46)
    at Array.webpackJsonpCallback [as push] (:3000/static/js/bundle.js:33)
    at :3000/static/js/main.chunk.js:1
index.js:1 The above error occurred in the <UserData> component:

    at UserData (http://localhost:3000/static/js/main.chunk.js:618:5)
    at div
    at App

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
console.<computed> @ index.js:1
react-dom.development.js:23275 Uncaught TypeError: io is not a function
    at Object.self.ioSetup (:3000/static/js/vendors~main.chunk.js:344)
    at Object.start (:3000/static/js/vendors~main.chunk.js:1208)
    at new UserData (:3000/static/js/main.chunk.js:625)
    at constructClassInstance (:3000/static/js/vendors~main.chunk.js:20992)
    at updateClassComponent (:3000/static/js/vendors~main.chunk.js:25617)
    at beginWork (:3000/static/js/vendors~main.chunk.js:27212)
    at HTMLUnknownElement.callCallback (:3000/static/js/vendors~main.chunk.js:12177)
    at Object.invokeGuardedCallbackDev (:3000/static/js/vendors~main.chunk.js:12226)
    at invokeGuardedCallback (:3000/static/js/vendors~main.chunk.js:12286)
    at beginWork$1 (:3000/static/js/vendors~main.chunk.js:32028)
    at performUnitOfWork (:3000/static/js/vendors~main.chunk.js:30864)
    at workLoopSync (:3000/static/js/vendors~main.chunk.js:30801)
    at renderRootSync (:3000/static/js/vendors~main.chunk.js:30767)
    at performSyncWorkOnRoot (:3000/static/js/vendors~main.chunk.js:30384)
    at scheduleUpdateOnFiber (:3000/static/js/vendors~main.chunk.js:29972)
    at updateContainer (:3000/static/js/vendors~main.chunk.js:33509)
    at :3000/static/js/vendors~main.chunk.js:34041
    at unbatchedUpdates (:3000/static/js/vendors~main.chunk.js:30524)
    at legacyRenderSubtreeIntoContainer (:3000/static/js/vendors~main.chunk.js:34040)
    at Object.render (:3000/static/js/vendors~main.chunk.js:34123)
    at Module.<anonymous> (:3000/static/js/main.chunk.js:387)
    at Module../src/index.js (:3000/static/js/main.chunk.js:476)
    at __webpack_require__ (:3000/static/js/bundle.js:857)
    at fn (:3000/static/js/bundle.js:151)
    at Object.1 (:3000/static/js/main.chunk.js:957)
    at __webpack_require__ (:3000/static/js/bundle.js:857)
    at checkDeferredModules (:3000/static/js/bundle.js:46)
    at Array.webpackJsonpCallback [as push] (:3000/static/js/bundle.js:33)
    at :3000/static/js/main.chunk.js:1

This means that your build step hasn't included the socket.io correctly and you haven't included it in your HTML as a script link.

Personally, I recommend starting by not including any of the non-REACT dependencies in your build. Keep them as separate script links in your HTML - before loading your REACT code.

Once you have that working, you can look at whether incorporating any of the other libraries into your build is worth the effort. In my view it rarely is.

As far as I can see, that repo has some issues. Not least a reference to a local version of uibuilder whose path might not be correct in all cases.

I also cant see how it includes the Socket.io client.

The package.json proxy setting also has some limitations that you need to understand. It only works if you started node-red with npm start from the folder containing the package.json file for example. In addition, the proxy is incorrect if you have made use of the v4 custom server feature.

That shouldn't be needed and is dangerous in itself because you will eventually end up with different client and server versions. Always use uibuilder's version of Socket.io to avoid this. The socket.io client is always served up by uibuilder and is always accessible on the URL ../uibuilder/vendor/socket.io/socket.io.js. If you really want to include that in your build, you should look up the correct way to do it and pay special attention to relative folder paths on the server because they can change depending on how you have things installed. And as mentioned, the chances of you getting any performance benefits from including the libraries in your build are pretty minor. Mostly gone are the days where having more than 1 request to the same host caused performance issues. Keep it simple, at least to start with.

Indicates that the build went wrong or something didn't load as expected. Check the network tab to see if something didn't load. If everything loaded, something went wrong with the build.

Again though, you really aren't getting any benefits from including the uibuilderfe library in your build. Use the .min version of the library if you want to reduce the load size a little. But don't even do that until everything is working.

Yes, your build failed to include the socket.io client library.

The template you reference was built using create-react-app - I just took a look at it and tried to build the demo app. What a bag of nails!

It is appalling in my view. It is full of hidden config and confusing boilerplate.

I've really not time to mess with it I'm afraid. If you do manage to get something working, please do consider putting a template together or a WIKI article at least so that others can benefit.

What I did get working was this:

index.html:

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Node-RED UI Builder - REACTjs template</title>
    <meta name="description" content="Node-RED UI Builder - REACTjs template">
    <link rel="icon" href="./images/node-blue.ico">

    <link type="text/css" rel="stylesheet" href="./index.css" media="all">

</head><body>
    
    <h1>uibuilder REACTjs Template</h1>
    <p>Based on React in One Minute</p>
    <p>This page demonstrates using React with no build tooling.</p>
    <p>React is loaded as a script tag.</p>

    <!-- We will put our React component inside this div. -->
    <div id="like_button_container"></div>

    <pre id="msg" class="syntax-highlight">Waiting for a message from Node-RED</pre>

    <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
    <script src="./uibuilderfe.min.js"></script>
    
    <!-- Load React. -->
    <!-- Note: when deploying, replace "development.js" with "production.min.js". -->
    <script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>

    <script src="./index.js"></script>

</body></html>

index.js:

/* jshint browser: true, esversion: 6, asi: true */
/* globals uibuilder */
// @ts-nocheck
'use strict'

/** Minimalist code for uibuilder and Node-RED */

// return formatted HTML version of JSON object
function syntaxHighlight(json) {
    json = JSON.stringify(json, undefined, 4)
    json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
    json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
        var cls = 'number'
        if (/^"/.test(match)) {
            if (/:$/.test(match)) {
                cls = 'key'
            } else {
                cls = 'string'
            }
        } else if (/true|false/.test(match)) {
            cls = 'boolean'
        } else if (/null/.test(match)) {
            cls = 'null'
        }
        return '<span class="' + cls + '">' + match + '</span>'
    })
    return json
} // --- End of syntaxHighlight --- //

const e = React.createElement

class LikeButton extends React.Component {
  constructor(props) {
    super(props)
    this.state = { liked: false }
  }

  render() {
    if (this.state.liked) {
      return 'You liked this.'
    }

    return e(
      'button',
      { onClick: () => {
          this.setState({ liked: true }) 
          uibuilder.send({payload: {liked: true}})
      }},
      'Like'
    );
  }
}

const domContainer = document.querySelector('#like_button_container')
ReactDOM.render(e(LikeButton), domContainer)


// run this function when the document is loaded
window.onload = function() {
    // Start up uibuilder - see the docs for the optional parameters
    uibuilder.start()

    // Listen for incoming messages from Node-RED
    uibuilder.onChange('msg', function(msg){
        console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)

        // dump the msg as text to the "msg" html element
        const eMsg = document.getElementById('msg')
        eMsg.innerHTML = syntaxHighlight(msg)
    })
}

That sends the button data back to Node-RED. You can send a msg from node-red to the page as well but that isn't actually integrated to REACT. But it shows the way - I don't have time to work out the exact REACT code I'm afraid.

This did remind me why I didn't choose REACT as the default template when I chose VueJS. And I am glad I made that decision.