Go with React

Once upon a time, I was a JavaScript developer. This was the pre-ES6 days when JS was horrible in every sense of the word. It’s now slightly better — but only marginally. Since then, my career forayed to iOS and backend development and I never looked back at weakly-typed languages. It goes without saying that I didn’t miss the subtle production-bugs that would surface when you least expect it.

Recently I was given the task of creating a cross-platform Desktop application. Time was tight so I explored Electron. Electron uses a Chromium browser window to “simulate” a desktop application window. This means your UI has to be programmed in HTML and Javascript. There was no way I was going to travel down that road again so I picked up my friend’s Typescript book and read it.

I was not dazzled by Typescript, but neither was I unimpressed. My instincts told me I could do better. My backend language of choice is Go. For baking servers, you’d be hard-pressed to find a better alternative. I thought before I go knee-deep into Typescript, I would explore the gopher.js project. Like Typescript, gopher.js transpiles Go code to ES5 JavaScript. This time I was dazzled. Coincidently I also watched Paul Jolly’s presentation at GopherCon UK where he showcased his Go bindings for React.

Standing on the shoulders of Giants

I was initially intrigued by his go generate approach to the problem. It felt novel. I’m also a sucker for fancy English accents.

After a week or two of writing my application, I kept hitting roadblocks time and time again. I was continually colliding with the package just to get simple things done. His package simply wasn’t flexible enough. I provided a PR for one issue, but after a month of waiting, I realized that he was too busy to review it. I had many other issues too, but the inner workings of his package require careful study and I was hesitant to maintain a private fork.

After waiting far longer than I should have, I went to Plan B.

Plan B: Do it myself.

I learned a lot from studying his package. I knew there were quite a few good parts. I jotted down the good, the bad and the ugly. The plan was to keep the good, minimize the bad and eradicate the ugly. That meant go generate had to go!

So I created React [Documentation]. It is production-ready.

It’s light-weight and intuitive. If you have experience with React, then my package can really improve productivity. Since it’s a very thin wrapper over the existing React API, I promise it will never conflict with whatever it is you are trying to do.

Tutorial — Uptime timer

There are 2 examples provided. The second example explores more advanced topics such as handling events and creating a Ref to deal with DOM objects directly. I will discuss only the first in this article. Furthermore, it is assumed that you are already familiar with React.js, Go and to a lesser extent, gopher.js.

The Goal

Make a webpage that indicates the uptime starting from when the website loaded. It should refresh every 1 second.

Preliminary Design Decisions

  • The component will be passed a property which will record when the webpage was loaded.
  • The component will maintain a state which will record the elapsed time since when the webpage was loaded
  • The component will display the elapsed time (which will be the uptime)

Create the definition of your props and state

type TimerProps struct {
StartTime int64 `react:"start"`
}
type TimerState struct {
Elapsed int64 `react:"elapsed"`
}

The react struct tag is used to convert the fields into the keys of the map which will be passed by gopher.js to the react library. map[string]interface{} maps to a JS object from React’s point of view.

Create your “class” definition

<timer_def.go>
var TimerComponent *js.Object
func init() {
   timerDef := react.NewClassDef("Timer")
   ...

TimerComponent = react.CreateClass(timerDef)
}

The code above is equivalent to an ES6 class. Ironically, the package uses a form closer to the pre-ES6 style. TimerComponent can be passed directly toReactDOM.render(), if we were thinking in terms of vanilla React usage.

Set the initial state

timerDef.GetInitialState(func(this *js.Object, props react.Map) interface{} {
   var tProps TimerProps
react.HydrateProps(this, &tProps)
   return TimerState{Elapsed: time.Now().Unix() - tProps.StartTime}
})

The snippet above uses the props to help seed the initial state. This is acceptable usage of what would otherwise be bad practice.

You can see that we use the HydrateProps() function to Unmarshal our props struct.

You can also access the props directly from the props argument.

We finally return the initial state of the component.

Kickstart a timer

We want a timer to periodically call a function to “refresh” our state which records the elapsed time since loading.

timerDef.ComponentDidMount(func(this *js.Object, props, state react.Map, setState react.SetState) {

this.Set("timer", react.JSFn("setInterval", this.Get("tick"), 1000))
})

You can see that we use the JSFn function to call native JavaScript functions. In this case, the timer will call the tick method of our “class” every 1 second.

The ComponentDidMount lifecycle method is called when the component is first mounted onto the DOM.

Define the tick method

This tickmethod gets called by the timer. This is where we update the state.

timerDef.SetMethod("tick", func(this *js.Object, props, state react.Map, setState react.SetState, arguments []*js.Object) interface{} {
   var tProps TimerProps
react.HydrateProps(this, &tProps)
   elapsed := time.Now().Unix() - tProps.StartTime
   // Update state
setState(TimerState{Elapsed: elapsed})
   return nil
})

You can see that once again we are using the HydrateProps() function to Unmarshal the props to our TimerProps struct.

You can see that once the elapsed time is determined, we then update the state.

Rendering the Output

timerDef.Render(func(this *js.Object, props, state react.Map) interface{} {
   var tState TimerState
react.HydrateState(this, &tState)
   text := "Uptime counter:" + strconv.Itoa(int(tState.Elapsed))
   return elements.H3(&elements.H3Props{Style: &elements.Styles{TextAlign: "center"}}, text)
})

This is arguably the most important lifecycle method. We access the current state (which holds the elapsed valued) and compile a human-readable string.

We then return an H3 element contained in the elements sub-package.

The H3 function is equivalent to writing <h3></h3> in JSX. The props are h3-specific props (found in the elements sub-package). The final argument is the children — which in this case is merely a human-readable string.

Final Step — Attach component to DOM

import (
"github.com/rocketlaunchr/react"
"honnef.co/go/js/dom"
)
var document = dom.GetWindow().Document()
func main() {
domTarget := document.GetElementByID("app")
   react.Render(react.JSX(TimerComponent, &TimerProps{StartTime: time.Now().Unix()}), domTarget)
}

We have a div in our html file with an id set to "app". We render our top-level React component to our DOM target and presto:

Final Results

Example 1
Example 2

Final notes

If you are the CTO of a small software firm and you are constantly juggling your limited resources with ever-increasing demands, then I personally believe you should make the transition to Go for your backend stack. Our firm went from PHP to Go, and it has done nothing short of miracles in terms of performance and robustness. Go programming — at a structural level, enforces good habits (error checking, unit testing etc), which in turn reduces bugs.

License

The package is provided with a modified MIT license (mMIT). It has all the freedoms that the standard MIT license has, but with the additional constraint that the package not be used intentionally for military purposes.

Project Location

React [Documentation]