Skip to content
JoshuaUwuseba edited this page Sep 2, 2020 · 23 revisions

NOTE: Please note that I don't fully understand ReactJS, especially the component model. This example was originally provided by someone else and I've tweaked it for uibuilder v2. However, I note that it doesn't exactly work as expected. If you know how to improve this example, please let me know or simply edit this WIKI article.

Example: ReactJS

This is a quick introduction on how you can use Node-red, uibuilder and react all together.

Installation & Configuration

NB: I will assume that you are using Linux and have installed Node-RED in the default way (globally). I also assume that your <userDir> folder is in the default location (~/.node-red) and that you are not using projects. Adjust the example paths if some of that isn't true.

  • Install uibuilder from the Node-RED palette menu.
  • Add the "vue" example from the import menu.
  • Change the name and url in the resulting node to something helpful to you - this example will use the URL react_ui.
  • Under "Advanced Settings", turn off "Copy index ...." because we won't be using those files.
  • Deploy.
  • Use uibuilder's library manager to install reactjs.

Unfortunately, REACT is too complex to allow everything to be done from within the Node-RED Editor. So from here, you need a terminal session on your server so that you can run command lines.

  • npm install -g create-react-app This lets you create a complete REACT app and includes a suitable build step. See GitHub for more details on this app.

  • cd ~/.node-red/uibuilder/react_ui.

  • create-react-app myapp This creates a new sub-folder called myapp, we will be building the app in there.

  • Your app now needs access to the uibuilderfe.js file from the uibuilder package. You can copy it over manually but you would need to remember to do that whenever it is updated. Much better is to create a soft-link

    From: ~/.node-red/node_modules/node-red-contrib-uibuilder/nodes/src

    To: ~/.node-red/uibuilder/react_ui/myapp/src/libs/uibuilder/.

  • Edit the files listed below copy the code given here into the appropriate file.

  • Create an environment variable called PUBLIC_URL and set it to the value .. If you don't do this, your resulting web app will not be able to load its resources, you will see the errors in your browser console.

  • npm run build Assuming all goes well, your ready-to-use app will now exist in ~/.node-red/uibuilder/react_ui/myapp/build.

  • You now have several options. The quickest is to copy the contents of build to ~/.node-red/uibuilder/react_ui/src.

    NOTE: It would be better if you create a soft-link from the ~/.node-red/uibuilder/react_ui/myapp/build folder to the ~/.node-red/uibuilder/react_ui/src folder since that will let you rebuild without having to remember to copy the resulting build files manually.

  • You can now open the react_ui url (most easily done by opening the configuration editor of the uibuilder node and clicking on the link) - try sending a msg to it and you should see the information at the bottom of the page update.

The rest is up to you! See the VueJS examples to see how to use the different features of the uibuilder library.

myapp/package.json

Add the following line:

"homepage":"/react_ui"

myapp/public/index.html

<!DOCTYPE html>
<html lang="en"><head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">

    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

    <title>React UI - uibuilder and Node-RED</title>
</head><body>

    <div>
        <h1>
            Welcome to REACT-UIbuilder for Node-RED!
        </h1>
    </div>
    <div id="root">
        <div id="app">
        </div>
    </div>

</body></html>

myapp/src/App.js

import React from 'react';
import './App.css';

// Import uibuilder enabled component
import UserData from './scenes/UserData';

function App() {
  return (
    <div className="App">

        {/* THIS IS THE UIBUILDER COMPONENT */}
        <UserData title="User Data"></UserData>

    </div>
  );
}

export default App;

myapp/src/App.css

body {
    margin: 1rem;
    padding: 1rem;
}

.d1 {
  margin: 0.5rem;
  padding: 0.5rem;
}

myapp/src/scenes/UserData.js

/* This is where the uibuilder action happens */
import React, { Component } from 'react';
//import ReactDOM from 'react-dom';
//import { findDOMNode } from 'react-dom';

import uibuilder from '../libs/src/uibuilderfe.js'

class UserData extends Component{
	constructor(props){
		super(props)

        /** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
         * Pass the namespace and ioPath variables if hosting page is not in the instance root folder
         * e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
         * e.g. uibuilder.start('/nr/uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names
         */
        uibuilder.start();

		this.state = {
            // Example of retrieving data from uibuilder
            feVersion   : uibuilder.get('version'),

            socketConnectedState : false,
            serverTimeOffset     : '[unknown]',
    
            msgRecvd    : '[Nothing]',
            msgsReceived: 0,
            msgCtrl     : '[Nothing]',
            msgsControl : 0,
    
            msgSent     : '[Nothing]',
            msgsSent    : 0,
            msgCtrlSent : '[Nothing]',
            msgsCtrlSent: 0,    
        }
        
        /** You can use the following to help trace how messages flow back and forth.
         * You can then amend this processing to suite your requirements.
         */

        //#region ---- Trace Received Messages ---- //
        // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
        // newVal relates to the attribute being listened to.
		uibuilder.onChange('msg', (newVal) => {

			this.setState({ 'msgRecvd': newVal });
            this.forceUpdate();
            
			console.info('[uibuilder.onChange] msg received from Node-RED server:', newVal);
		})

        // As we receive new messages, we get an updated count as well
		uibuilder.onChange('msgsReceived',(newVal) =>{
            console.info('[uibuilder.onChange] Updated count of received msgs:', newVal);
            
			this.setState({ 'msgsReceived': newVal });
            this.forceUpdate();
		})

        // If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
        uibuilder.onChange('ctrlMsg', (newVal) => {
            console.info('[uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', newVal);

            this.setState({ 'msgCtrl': newVal });
            this.forceUpdate();
        })
        // Updated count of control messages received
        uibuilder.onChange('msgsCtrl', (newVal) => {
            console.info('[uibuilder.onChange:msgsCtrl] Updated count of received CONTROL msgs:', newVal);

            this.setState({ 'msgsControl': newVal });
            this.forceUpdate();
        })
        //#endregion ---- End of Trace Received Messages ---- //

        //#region ---- Trace Sent Messages ---- //
        // You probably only need these to help you understand the order of processing //
        // If a message is sent back to Node-RED, we can grab a copy here if we want to
        uibuilder.onChange('sentMsg', (newVal) => {
            console.info('[uibuilder.onChange:sentMsg] msg sent to Node-RED server:', newVal);

            this.setState({ 'msgSent': newVal });
            this.forceUpdate();
        })
        // Updated count of sent messages
        uibuilder.onChange('msgsSent', (newVal) => {
            console.info('[uibuilder.onChange:msgsSent] Updated count of msgs sent:', newVal);

            this.setState({ 'msgsSent': newVal });
            this.forceUpdate();
        })

        // If we send a control message to Node-RED, we can get a copy of it here
        uibuilder.onChange('sentCtrlMsg', (newVal) => {
            console.info('[uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', newVal);

            this.setState({ 'msgCtrlSent': newVal });
            this.forceUpdate();
        })
        // And we can get an updated count
        uibuilder.onChange('msgsSentCtrl', (newVal) => {
            console.info('[uibuilder.onChange:msgsSentCtrl] Updated count of CONTROL msgs sent:', newVal);

            this.setState({ 'msgsCtrlSent': newVal });
            this.forceUpdate();
        })
        //#endregion ---- End of Trace Sent Messages ---- //

        // If Socket.IO connects/disconnects, we get true/false here
        uibuilder.onChange('ioConnected', (newVal) => {
            console.info('[uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', newVal)

            this.setState({ 'socketConnectedState': newVal })
            this.forceUpdate();
        })
        // If Server Time Offset changes
        uibuilder.onChange('serverTimeOffset', (newVal) =>{
            console.info('[uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', newVal)

            this.setState({ 'serverTimeOffset': newVal })
            this.forceUpdate();
        })

        // 	//Manually send a message back to Node-RED after 2 seconds
        // 	window.setTimeout(function(){
        // 	    console.info('Sending a message back to Node-RED-after2sdelay')
        // 	    uibuilder.send({'topic':'uibuilderfe','payload':'I am a message sent from the uibuilder front end'})
        // 	},2000)
	}


	render(){
		return(

			<div ref="root" style={{height:"50vh"}}>
                <hr></hr>
                <div className="d1">
                    <div>Last msg Received:</div>
                    <pre><code>{JSON.stringify(this.state.msgRecvd, null, 2)}</code></pre>
                    <div># Msgs Received: {this.state.msgsReceived}</div>
                </div>

                <div className="d1">
                    <div>last Ctl Msg Received:</div>
                    <pre><code>{JSON.stringify(this.state.msgCtrl, null, 2)}</code></pre>
                    <div># Control Msgs Received: {this.state.controlMsgsReceived}</div>
                </div>

                <div className="d1">
                    <div>last Msg Sent: {this.state.lastMsgSent}</div>
                    <div># msgs Sent: {this.state.msgsSent}</div>
                </div>

                <div className="d1">
                    <div>Socket Connected?: {this.state.socketConnectedState}</div>
                    <div>uibuilderfe Version: {this.state.feVersion}</div>
                    <div>Server Time Offset from browser: {this.state.serverTimeOffset}</div>
                </div>

			</div>
		);

	}


	shouldComponentUpdate(){
		//ascomponentisnotlinkedtotheexternaldata,thereisnoneedinupdates
		return false;
	}
}

export default UserData
Clone this wiki locally