Don’t Panic! React with Common Lisp

I’ve written Panic, a small library for generating React components with Common Lisp and Parenscript. The current version of Panic is compatible with React 0.11.2, but I’m working on support for React 0.14, in particular, for the changes introduced in React 0.12. Panic is not yet available for download via quicklisp.

API

The Panic API consists of two Parenscript macros, DEFCOMPONENT and JSL.

[Macro] defcomponent name lambda-list [documentation] form*

Defines a Parenscript special variable as if by PS:DEFVAR, assigning it the value created by a call to React.createClass(…).

name is a symbol.

lambda-list is a list the elements of which are a list matching the destructuring lambda list

(&key display-name get-initial-state get-default-props prop-types mixins statics component-will-mount component-did-mount component-will-receive-props should-component-update component-will-update component-did-update component-will-unmount)

If display-name is not provided, it will default to the symbol name of name.

The remaining elements must be expressions yielding functions–the lifecycle methods of the React component class.

forms is an implicit PROGN and constitutes the body of the mandatory render method of the React component class.

[Macro] jsl form* => result*

JSL is a Lispy take on JSX, the React Javascript syntax extension. JSL transforms its body, expanding JSL forms into Parenscript function forms to call React DOM operations.

A JSL form is a list with the first element being a keyword naming a React element type. All but the last of the remaining elements constitute a possibly empty list of alternating React property names and values. The last element is a possibly empty list of the children of the React element.

Example: A Simple Component

(ps:ps (panic:defcomponent hello-message () (panic:jsl (:div "Hello " (ps:@ this props name)))) (ps:chain -react (render-component (hello-message (ps:create :name "John")) (ps:chain document (get-element-by-id "mount-node")))))

Example: A Stateful Component

(ps:ps (panic:defcomponent timer (:get-initial-state #'(lambda () (ps:create 'seconds-elapsed 0)) :tick #'(lambda () (ps:chain this (set-state (ps:create 'seconds-elapsed (1+ (ps:@ this state seconds-elapsed)))))) :component-did-mount #'(lambda () (setf (ps:@ this interval) (set-interval (ps:@ this tick) 1000))) :component-will-unmount #'(lambda () (clear-interval (ps:@ this interval)))) (panic:jsl (:div "Seconds Elapsed: " (ps:@ this state seconds-elapsed)))) (ps:chain -react (render-component (timer) (ps:chain document (get-element-by-id "mount-node")))))

Example: An Application