Skip to content

Latest commit

 

History

History
99 lines (70 loc) · 3.72 KB

README.md

File metadata and controls

99 lines (70 loc) · 3.72 KB

jscl-react is a library for writing React components in common lisp, using jscl

Here's a quick demo:

The demo application is available to try out here

Reference app demos

I've recreated two of the official React example applications in jscl-react to show it off

Emoji Search

Original project: ahfarmer/emoji-search, running live here

jscl-react version running here, source code in the emoji-search subdirectory

The original project uses import on a json file to make the contents of that file available at runtime, embedded in the code via Babel. I do the same, by loading cl-json at compile time using quicklisp, and storing the parsed contents of the json file in a variable that is then available at runtime. You can see exactly how I did this in emoji-search/filter-emoji.lisp. Alternative ways to do it could have included loading it into a variable as a string and using the browser's JSON.parse at runtime, which would have eliminated the need for quicklisp, or fetching it at runtime using the browser's built-in fetch or XMLHttpRequest.

Counter App

Original project: arnab-datta/counter-app, running live here

jscl-react version running here, source code in the counter-app subdirectory

Creating React elements

The shortcut react-create-element is provided for the JavaScript function React.createElement. For example:

(react-create-element
	"div"
	#()
	(react-create-element "h1" #() "I'm big!")
	"I'm a text node!"
	(react-create-element "br")
	(react-create-element "button" (object "onClick" #j:window:alert) "Click me!"))

The macro render is provided to recurse on child elements, where appropriate. The above call can be rewritten as:

(render
	"div"
	#()
	("h1" #() "I'm big!")
	"I'm a text node!"
	("br")
	("button" (object "onClick" #j:window:alert) "Click me!"))

Mounting React elements

Given a mount element like a div with an id, you can place a react element in it like so

(mount "the-id" (react-create-element "h1" #() "Hello, world!"))

Defining a component is as simple as this:

(defcomponent (btn 0) (set-state state props)
  (let ((handler (lambda (event)
                   (funcall set-state (1+ state)))))
    (render
      "button"
      (object "onClick" handler)
      "Hello, world: "
      state)))

Here, btn is the name of the component, and 0 is the initial state. The rest of the definition is the render function for the component, accepting set-state (the function called to update the state), state, and props.

This component's state is a single number. Clicking the returned button increments the state by 1, which is reflected in the text of the button

The name of the component should be invoked to create one, which can then be passed to create-react-element or render. So we can render a btn to the screen like so

(mount "root" (render
	"div"
	#()
	("h3" #() "Here is my cool button:")
	((btn))
	"Isn't it pretty?"))

Properties should be passed in the way React.createElement expects them, like so

(mount "root" (render
	"div"
	#()
	("h3" #() "Here is my cool button:")
	((btn) (object "id" "the-button" "className" "pretty-button"))
	"Isn't it pretty?"))