B4J Tutorial [BANanoReact] Render your Website/WebApp using Facebook ReactJS

Mashiane

Expert
Licensed User
Hi there

DEPRECATED: THIS WAS JUST A PROOF OF CONCEPT (now overtaken by BANanoWebix & BANanoVueMaterial)


Before you embark on this journey, you might first want to look at BANano4DummiesByExample first!

You are going to need some resources from GitHub, here is the link, BANanoReActApp

github.png

Reproduction after you have downloaded

1. Copy the contents of the Libraries folder to your B4J external libraries folder.
2. If you want to recompile BANanoReact yourself, you can do that with the BANanoReact project.
3. The demo project on using BANanoReact has 4 examples, these are on the BANanoReactDemo folder.
4. BANanoReactMDL is a wrapper of Google's MDL, whilst that framework is no longer supported by Google (they are working on V2 of it using MDC, it is stable to create websites & webapps.
5. BANanoReactMDLDemo is a demo app based on BANanoReactMDL.

When ran, the BANanoReactDemo should show something this similar. This has been ran inside Google Mobile Shell.

*** IMPORTANT ****

NOT all React functionality has been wrapped for this library. The crux was just for view rendering.

The most updated source code for all examples is in the GitHub Repo.

AllExamples.gif




Introducing React

At it’s most basic, React is a tool for rendering HTML with JavaScript. ReactDOM.render takes a ReactElement object describing what to render, adding the result to the given DOM node. But how do you create ReactElement objects? That’s where React.createElement comes in.

React.createElement takes three parameters, and returns a ReactElement. From the React documentation:

createElement(string/ReactClass type, [object props], [children ...]) -> ReactElement

The type argument lets us specify what type of HTML element to use; it also lets us specify custom elements. The props argument is a way of specifying which attributes are defined on the HTML element, e.g. href for anchors. Finally, the children arguments are strings or ReactElement objects (or arrays of the same) which will be used for the returned element’s content. By choosing to omit or specify children, you can return a single ReactElement or an entire tree.

Since createElement is just plain JavaScript, you can mix in loops, if statements, and anything else JavaScript allows. You can also easily substitute in data stored in JSON.

About BANanoReact

BANanoReact seeks to wrap some React functionality to be able create React Apps using BANano without JSX. I wish this could be a community project. When it comes to ReAct, everyone is all about using JSX and for newbies this can get to be very overwhelming. The sad reality everywhere we go, most of the tech industry is talking about react. I recently went to a job interview and this is one of the things that was asked. Can you React? I responded No, but I can learn.

One can develop apps using Facebook's React without JSX, however the documentation to do this is very scarce and limited. This has brought me to this point. I've just spent the last 3 hours googling and referencing and looking for easier methods of just creating a simple Hello World project using BANano.

Apparently, there are JSX convectors which give one a vanilla javascript version of the code. Im yet to explore this.

Reproduction: Get the CDN version of the framework and add these in AppStart.

B4X:
BANano.Header.AddJavascriptFile("react.development.js")
    BANano.Header.AddJavascriptFile("react-dom.development.js")
Add this code in Main.BANano_Ready or in another code module..

B4X:
'Static code module
Sub Process_Globals
    Private BANano As BANano
    Private body As BANanoElement

    Public React As BANanoObject
    Public ReactDOM As BANanoObject
End Sub

Sub Show
    'initialize the react library
    React.Initialize("React")
    'initialize the react dom library
    ReactDOM.Initialize("ReactDOM")
    '
    'get the body of the page
    body = BANano.GetElement("#body")
    'clear the body
    body.Empty

    'create a parent container div on the body: recommended
    Dim rootm As BANanoHTML
    rootm.Initialize("div").SetID("react-root")

    'add the div to the body, get it and convert it to an object
    Dim root As BANanoObject = body.Append(rootm.HTML).Get("#react-root").ToObject

    'execute react to create the element
    Dim hw As BANanoObject = React.RunMethod("createElement", Array("div", CreateMap(), "Hello World!"))
    'render the element
    ReactDOM.RunMethod("render", Array(hw, root))
End Sub
Looks simple isn't it?

A lot can happen here, its a rabbit hole that we can explore...

Enjoy!
 
Last edited:

Mashiane

Expert
Licensed User
Let's try and add a button and trap its event

ReActButton.gif


One of the things we cannot miss when doing apps are events, here is a button with a click event.

B4X:
'create a button, set its class properties
    Dim btnOpt As Map = CreateMap("className": "btn btn-large")
    'add a click event
    Dim e As BANanoEvent
    btnOpt.Put("onClick", BANano.CallBack(Me, "next_click", e))
    'create the element
    Dim btn As BANanoObject = React.RunMethod("createElement", Array("button", btnOpt, "Next"))
The button is added after the div for now. This means we just update our previous render method and append it with btn.

B4X:
'execute react to create the element
    Dim hw As BANanoObject = React.RunMethod("createElement", Array("div", CreateMap(), "Hello World!", btn))
We defined a callback for the button, let's see its code.

B4X:
Sub next_click(e As BANanoEvent)
    Log(e)
    BANano.Window.Alert("Next Click!")
End Sub
 

Attachments

Mashiane

Expert
Licensed User
Let's add a component and set its style

welcome1.png



B4X:
Dim welcomeOpt As Map = CreateMap()
    welcomeOpt.Put("style", CreateMap("color":"red"))
    Dim welcome As BANanoObject = React.RunMethod("createElement", Array("h1", welcomeOpt, "Welcome to the react world!"))
Again, we append this to the root element..

B4X:
Dim hw As BANanoObject = React.RunMethod("createElement", Array("div", CreateMap(), "Hello World!", btn, welcome))
 

Mashiane

Expert
Licensed User
Lets create some classes to handle both the ReactElement and React & ReactDom.

listit.png


Here we will create a div, add a h1 to it and then create a UL and add some LI items to it.

Originally, this is the code to create this..

reactcode.png


This is indeed some kind of tree structure, what we do it then just break it down further bit by bit and

1. Create each item individually
2. Add each item to its parent in sequence.

B4X:
'initialize banano react
    BR.Initialize
    '
    'create a div
    Dim div As ReactElement
    div.Initialize(BR, "div")
    'create a h1 with text contacts
    Dim h1 As ReactElement
    h1.Initialize(BR, "h1").AddChild("Contacts")
    'add h1 to div   
    div.AddReactElement(h1)
    'create a ul
    Dim ul As ReactElement
    ul.Initialize(BR, "ul")
    'create a list item
    Dim li1 As ReactElement
    li1.Initialize(BR, "li")
    'create an anchor and set properties
    Dim a1 As ReactElement
    a1.Initialize(BR, "a").AddChild("James Nelson").SetProp("href","mailto:james@frontarm.com").AddToParent(li1)
    '
    Dim li2 As ReactElement
    li2.Initialize(BR, "li")
    Dim a2 As ReactElement
    a2.Initialize(BR, "a").AddChild("Mashy").SetProp("href","mailto:mbangaa@gmail.com").AddToParent(li2)
    '
    ul.AddReactElement(li1)
    ul.AddReactElement(li2)
    '
    div.AddReactElement(ul)
    '
    BR.Render(div, rr)
Code in post #1
 

Mashiane

Expert
Licensed User
A more streamlined version

So I decided to test another method today after I got some inspiration for the same example above. Instead of having to enclose "a", "li", "h1" etc, why not I just create a function for it.

So in the BANanoReact class..

B4X:
Sub div As ReactElement
    Dim d As ReactElement
    d.Initialize(Me, "div")
    Return d
End Sub
and then creating it in my module...

B4X:
'create a div
    Dim div As ReactElement = BR.div
#Updated code base on first post.
 

Mashiane

Expert
Licensed User
Just decided to add a CreateElement method to the BANanoReact class.

B4X:
'create an element
Sub CreateElement(typeOf As String, props As Map, children As Object) As BANanoObject
    Dim obj As BANanoObject
    obj = React.RunMethod("createElement", Array(typeOf, props, children))
    Return obj
End Sub
So that in our example code, we can just execute this...

B4X:
'create a list item
    Dim li1 As ReactElement = BR.li
    li1.AddChild(BR.CreateElement("a", CreateMap("href":"mailto:james@frontarm.com"),"James Nelson"))
Another option is having a CreateReactElement method..

B4X:
'create an instance of the reactelement
Sub CreateReactElement(typeOf As String, props As Map, children As Object) As ReactElement
    Dim obj As ReactElement
    obj.Initialize(Me, typeOf)
    obj.SetProps(props)
    obj.AddChild(children)
    Return obj
End Sub
This enables us to add an element like...

B4X:
'create a list item
    Dim li1 As ReactElement = BR.li
    li1.AddReactElement(BR.CreateReactElement("a", CreateMap("href":"mailto:james@frontarm.com"),"James Nelson"))
 
Last edited:

micro

Well-Known Member
Licensed User
Hi Mashy
If if I want to add a complete class for a element (example on a list 'ul") or another html object how can I do?
Example this class (for a list "ol") how do i add it?
B4X:
.rectangle {
counter-reset: li;
list-style: none;
font: 14px "Trebuchet MS", "Lucida Sans";
padding: 0;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
}
.rectangle a {
position: relative;
display: block;
padding: .4em .4em .4em .8em;
margin: .5em 0 .5em 2.5em;
background: #D3D4DA;
color: #444;
text-decoration: none;
transition: all .3s ease-out;
}
.rectangle a:hover {background: #DCDDE1;}       
.rectangle a:before {
content: counter(li);
counter-increment: li;
position: absolute;
left: -2.5em;
top: 50%;
margin-top: -1em;
background: #9097A2;
height: 2em;
width: 2em;
line-height: 2em;
text-align: center;
font-weight: bold;
}
.rectangle a:after {
position: absolute;
content: "";
border: .5em solid transparent;
left: -1em;
top: 50%;
margin-top: -.5em;
transition: all .3s ease-out;
}
.rectangle a:hover:after {
left: -.5em;
border-left-color: #9097A2;
}
Html for the list
B4X:
<ol class="rectangle">
  <li><a href="#">Item1</a></li>
  <li><a href="#">Item2</a></li>
  <li><a href="#">Item3</a></li>
  <li><a href="#">Item4</a></li>
  <li><a href="#">Item5</a></li>
</ol>
Thanks
 

Kiffi

Well-Known Member
Licensed User
@micro :

You can put your CSS in a #If CSS block inside the *_Ready() - Sub:

B4X:
#If CSS

  .rectangle {
    counter-reset: li;
    list-style: none;
    font: 14px "Trebuchet MS", "Lucida Sans";
    padding: 0;
    text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
  }

  .rectangle a {
    position: relative;
    display: block;
    padding: .4em .4em .4em .8em;
    margin: .5em 0 .5em 2.5em;
    background: #D3D4DA;
    color: #444;
    text-decoration: none;
    transition: all .3s ease-out;
  }

  .rectangle a:hover {
    background: #DCDDE1;
  }

  .rectangle a:before {
    content: counter(li);
    counter-increment: li;
    position: absolute;
    left: -2.5em;
    top: 50%;
    margin-top: -1em;
    background: #9097A2;
    height: 2em;
    width: 2em;
    line-height: 2em;
    text-align: center;
    font-weight: bold;
  }

  .rectangle a:after {
    position: absolute;
    content: "";
    border: .5em solid transparent;
    left: -1em;
    top: 50%;
    margin-top: -.5em;
    transition: all .3s ease-out;
  }

  .rectangle a:hover:after {
    left: -.5em;
    border-left-color: #9097A2;
  } 

#End If
Greetings ... Peter
 

micro

Well-Known Member
Licensed User
Thanks kiffi for your feedback but not work.

BANano_Ready is in Main and i put the CSS block
the "ol" element is in bas module ("pglessons2") in show Sub.
I did so:
ol.AddClass("rectangle")

It's correct?

P.S.
"ol" element is not present in BANanoReact.bas, insert:
B4X:
Sub ol As ReactElement
    Dim d As ReactElement
    d.Initialize(Me, "ol")
    Return d
End Sub
 

Mashiane

Expert
Licensed User
@micro, thanks for your question, kindly next time start a new thread for your question, you can prefix it with [BANanoReact].

In terms of what @Kiffi suggested, it is indeed the correct way of applying css to your BANano project and it works. I have also added the issue of inline css in my BANano4DummiesByExample thread.

React on the other hand works differently, there is an attribute called 'className' that is a replica of the 'class' attribute that should do the same.

As someone who is also experimenting on this React experiment with BANano, please let me revert back after I check your code. I'm sure we will find a solution.

Ta!
 

Mashiane

Expert
Licensed User
Lesson 5: Creating a google search form

BR_Lesson05.gif


In this example we create a form and then perform a google search from whatever we used as input in our form.

To do this we create a sub that renders our Button. The ErrorDisplay (red with yellow background) is just for the demo on how one can apply styles to elements.

B4X:
'creating a button sub
Sub Button(props As Map) As ReactElement
    Dim b As ReactElement = BR.button("").SetType("submit")
    b.AddChild(props.Get("label"))
    Return b
End Sub

Sub ErrorDisplay(msg As String) As ReactElement
    Dim d As ReactElement = BR.div("")
    d.SetStyle("color","red")
    d.SetStyle("backgroundColor","yellow")
    d.AddChild(msg)
    Return d
End Sub
To help with chaining in our class, we had to make some adjustments to the element creation code as a while, thus creating a div is now..

BR.div("")

B4X:
'get the body of the page
    body = BANano.GetElement("#body")
    'clear the body
    body.Empty
   
    'create a parent container div on the body: recommended
    Dim rrhtml As BANanoHTML
    rrhtml.Initialize("div").SetID("react-root")
    'add the div to the body, get it and convert it to an object
    Dim rr As BANanoObject = body.Append(rrhtml.HTML).Get("#react-root").ToObject
    '
    'initialize banano react
    BR.Initialize
    'create a form
    Dim form As ReactElement = BR.form("").SetTarget("_blank").SetAction("https://google.com/search")
    '
    Dim div1 As ReactElement = BR.div("").AddChild("Enter input and click Search")
    'create button
    Dim inp As ReactElement = BR.input("").SetName("q").SetClassName("input").SetPlaceholder("Search...")
    '
    form.AddReactElements(Array(div1,inp))
    form.AddReactElement(Button(CreateMap("label":"Search")))
    form.AddReactElement(ErrorDisplay("There are no android bots here!"))
   
    BR.Render(form, rr)
1. Above we clear the body of the page.
2. We create a root div that will house our page details. It is not recommended to render directly to the body.
3. We create a form, when submitted the form should open a blank page in our browser and the action to perform should executea google search.
4. We create div with text, 'Enter input..."
5. We create an input, set its name, and class and placeholder.
6. We add the div and input to the form
7. We add a button to the form, we have created a sub to build the button and then set the label property. One can build a component in this fashion.
8. We add the ErrorDisplay (just for show)
9. We render the form in the parent root div and run our app.

#Updated code on the first post.

PS: To set multiple styles one can use .SetStyles(map) and to set multiple properties one can use .SetProps(map)
 

Mashiane

Expert
Licensed User
At the heart of it, BANanoReact is just a helper library for one to render the UX. It does not have all the nitty gritties of React. As an example, here we have created the normal html elements that will enable one to create a website. Remember, one can use the following to make up their elements.

Major Changes:

  • CreateReactElement has been changed to CreateElement
  • AddReactElement has been changed to AddElement
  • AddReactElements has been changed to AddElements

SetClass - add a class to an element
SetStyle - add a css style to an element.

One can use these building blocks to create framework based elements.

bananoreact06.png


1. Create a new BANanoReact App. This gets the 'body' of the page and runs '.Empty' on it so that we are able to add our own details

B4X:
Dim app As ReactElement = BR.Initialize
2. Render Hello World! on the page (testing state). We have added an event to the header so that we trap it.

B4X:
    Dim h1 As ReactElement = BR.h1("").SetClassName("abc").SetTextAlign("center").SetOnClick(BANano.CallBack(Me,"h1_click",Null))
    h1.SetState("greeting", "World!")
    h1.AddChild("Hello " & h1.GetState("greeting"))
3. Add a nice paragraph

B4X:
Dim p1 As ReactElement = BR.p("").SetLabel("A nice text paragraph!")
4. Create a textbox

B4X:
Dim inp1 As ReactElement = BR.input("inp1").SetType("text")

5. Create a password input

B4X:
Dim pwd1 As ReactElement = BR.input("pwd1").SetType("password").SetDefaultValue("password")
6. Create a radio input

B4X:
Dim rad1 As ReactElement = BR.input("rad1").SetType("radio")
7. Create a checkbox

B4X:
Dim chk1 As ReactElement = BR.input("").SetType("checkbox")
8. Create a text-area

B4X:
Dim ta1 As ReactElement = BR.textarea("").SetDefaultValue("")
9. Create a file input

B4X:
Dim fil1 As ReactElement = BR.input("fil1").SetType("file")
10. Create a file input control

B4X:
Dim num1 As ReactElement = BR.input("num1").SetType("number")
11. Create a select / combo. We have used combo because 'select' is a b4j keyword.

B4X:
Dim sel1 As ReactElement = BR.combo("").SetDefaultValue("lime")
    Dim op1 As ReactElement = BR.option("").SetValue("grapefruit").SetLabel("Grapefruit")
    Dim op2 As ReactElement = BR.option("").SetValue("lime").SetLabel("Lime")
    Dim op3 As ReactElement = BR.option("").SetValue("coconut").SetLabel("Coconut")
    sel1.AddElements(Array(op1, op2, op3))
12. Create a button

B4X:
Dim btn As ReactElement = BR.button("").SetLabel("Button")
13. Create an image

B4X:
Dim imgx As ReactElement = BR.img("").SetSRC("./assets/image005.jpg")
14. Create a label

B4X:
Dim lbl1 As ReactElement = BR.label("").AddChild("This is a label!")
15. We then add all these elements to the app, we can create a div and render them on the div too.

B4X:
app.AddElements(Array(h1,p1,inp1,pwd1,rad1,ta1,fil1,num1,chk1,sel1,btn,imgx,lbl1))
16. We render all the elements to the body of the page.

B4X:
app.Render("body")
Our event to be trapped when we click on the header.

B4X:
Sub h1_click
    BANano.Alert("h1 clicked!")
End Sub
As most functionalities to handle the UX are already built within BANAno, getting / setting the element styles during app running is covered.
 

Mashiane

Expert
Licensed User
Let's assume you want to use the Bootstrap Framework.

1. Add it to Main.AppStart

B4X:
'add development react, replace these for production on app publish
    BANano.Header.AddJavascriptFile("react.production.min.js")
    BANano.Header.AddJavascriptFile("react-dom.production.min.js")
    '
    BANano.Header.AddCSSFile("bootstrap.min.css")
    BANano.Header.AddJavascriptFile("bootstrap.min.js")
    BANano.Header.AddJavascriptFile("jquery-3.3.1.slim.min.js")
    BANano.Header.AddJavascriptFile("popper.min.js")
You then write the code for the elements you need using the bootstrap syntax, for example in this case its the button.

B4X:
Sub Show
    'initialize the react library
    Dim app As ReactElement = BR.Initialize
    '
    Dim h1 As ReactElement = BR.h1("").SetLabel("Welcome to the react world!")
    h1.SetStyle("color", "red")
    
    'create a button, set its class properties
    Dim btnOpt As Map = CreateMap()
    'add a click event
    Dim e As BANanoEvent
    btnOpt.Put("onClick", BANano.CallBack(Me, "next_click", e))
    btnOpt.Put("className", "btn btn-primary")
    btnOpt.Put("type", "button")
    'create the element
    Dim btn As ReactElement = BR.CreateElement("button", btnOpt, "Next")
    '
    app.AddReactElement(h1)
    app.AddBR
    app.AddReactElement(btn)
    '
    app.Render("body")
End Sub

Sub next_click(e As BANanoEvent)
    Log(e)
    BANano.Window.Alert("Next Click!")
End Sub
This will display like this on google chrome iphone shell..

hellobootstrap.png
 

Mashiane

Expert
Licensed User
Detecting List Click Events

In previous examples we showed how to detect button click events, how about lists?

ListClickEVent.gif


To do this you need to define your list and your list items with an identifier i.e. id property.
We will then get the clicked anchor inside the list using a special method we have written.

If the target is an anchor, return its id, else, read is from parent.

B4X:
'get selected nav drawe item that was clicked
Sub GetIDFromEvent(e As BANanoEvent) As String
    'get the target
    Dim aid As String = ""
    Dim target As BANanoElement = BANano.ToElement(e.OtherField("target"))
    Dim tagName As String = target.GetField("tagName").Result
    Select Case tagName
        Case "A"
            aid = target.GetField("id").result
        Case Else
            Dim parentNode As BANanoElement = BANano.ToElement(target.GetField("parentNode"))
            aid = parentNode.GetField("id").result
    End Select
    Return aid
End Sub
1. So lets create our list and assign an event to it and also create the list items. We are not assigning any events to the child items of the list. Each item has a unique id.

B4X:
Sub Show
    'get the body of the page
    Dim app As ReactElement = BR.Initialize
    '
    'create an ol
    Dim ol As ReactElement = BR.ol("mylist").AddClass("rectangle")
    '
    Dim e As BANanoEvent
    Dim cb As BANanoObject = BANano.CallBack(Me, "ItemClick", Array(e))
    ol.SetOnClick(cb)
    
    'alternative
     'ol.SetOnClick1(Me, "itemclick")
   
    'create a list item
    Dim li1 As ReactElement = BR.li("").SetKey("li1")
    li1.AddElement(BR.CreateElement("a", CreateMap("href":"#", "id": "item1"),"Item 1"))
    '
    Dim li2 As ReactElement = BR.li("").SetKey("li2")
    li2.AddElement(BR.CreateElement("a", CreateMap("href":"#", "id": "item2"),"Item 2"))
    '
    Dim li3 As ReactElement = BR.li("").SetKey("li3")
    li3.AddElement(BR.CreateElement("a", CreateMap("href":"#", "id": "item3"),"Item 3"))
    '
    Dim li4 As ReactElement = BR.li("").SetKey("li4")
    li4.AddElement(BR.CreateElement("a", CreateMap("href":"#", "id": "item4"),"Item 4"))
    '
    Dim li5 As ReactElement = BR.li("").SetKey("li5")
    li5.AddElement(BR.CreateElement("a", CreateMap("href":"#", "id": "item5"),"Item 5"))
    '
    ol.AddElements(Array(li1,li2,li3,li4,li5))
    '
    '
    app.AddReactElement(ol)
    app.Render("app")
End Sub
We create the list and assign an event to it like this..

B4X:
Dim ol As ReactElement = BR.ol("mylist").AddClass("rectangle")
    '
    Dim e As BANanoEvent
    Dim cb As BANanoObject = BANano.CallBack(Me, "ItemClick", Array(e))
    ol.SetOnClick(cb)
We then trap the event by passing our special sub to it to get the item id.

B4X:
Sub ItemClick(e As BANanoEvent)
    Dim itemid As String = BR.GetIDFromEvent(e)
    Log(itemid)
End Sub
 
Last edited:
Top