• Stars
    star
    101
  • Rank 338,166 (Top 7 %)
  • Language
    Clojure
  • Created over 6 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A demo Single Page Application written in ClojureScript

Single Page Applications (SPAs) in ClojureScript, while often simple, are not always easy to understand. This example project demonstrates what I think is the best way to build SPAs today.

Screenshot

Usage

Make sure you have yarn and the Clojure CLI tools installed.

At development time, run

scripts/dev

# or to clean all build artifacts

scripts/dev --reset

to start a reloading figwheel server, then open http://localhost:9333/ in your browser.

To build a production build, run

scripts/prod

and open dist/index.html in the browser.

The script applies advanced optimizations by default. You can request a non-minified build using scripts/prod --simple.

Principles

  • Simplify tooling

    Apply the Unix philosophy: use simple tools based on abstractions that make sense. Everything is held together by duck tape (i.e. Bash scripts)

  • Data-driven routing

    Navigation is a concern of the M and C of your MVC application, not of the V, so routing should be decoupled from the view layer of your application.

  • Single global state

    A single global state atom is a simple and clear way to manage state. It makes it obvious where state resides. This project uses a single ratom, but to keep things simple it doesn't introduce a state management a la re-frame.

  • Explicit resource management

    When the user enters a page, the app needs to perform asynchronous side-effects (often network requests) and acquire resources (set up event listeners, timers, stateful objects). Conversely, resources need to be disposed of when leaving the page. Page resource management should be explicitly tied to navigation events, rather than component lifecycle methods.

  • Reloadability

    Hot reloading while keeping state is critical for developer productivity.

  • Embrace NPM

    ClojureScript rocks, but JavaScript reaches - and it has a powerful ecosystem. Because CLJS has great JS interop, prefer state-of-the-art NPM libraries over less powerful ClojureScript alternatives. Instead of CLJS-specific CLJSJS jars, directly tap NPM, the delivery path used by thousands of developers.

  • Embrace the Web Platform

    Modern JS engines ship with high-quality abstractions like fetch and ES6 promises. Use these over CLJS alternatives.

  • Error management

    Use Promise rejections to signal errors.

Technical details

Reagent

Reagent is deservedly the most popular React wrapper. With its syntax based on hiccup โ€” the s-expression syntax that in a dream world HTML would have used from the start โ€” and the Ratom reloading model, it is simple enough for beginners and flexible enough for experts.

Figwheel Main

Use Figwheel Main as the build tool. It's faster, cleaner and actively developed.

Webpack doublebundle

To require NPM dependencies (including, but not limited to, React components) with great reliability, use Webpack to create an auxiliary bundle and include it in the main build via foreign-libs. For more on the rationale, see this post and the official guide.

Router5

The Router5 library offers a data-centric and framework-agnostic routing system. In addition to being built on clean abstractions, it supports registering on-activate and on-deactivate hooks to trigger side-effects. A common use case is to load data when you enter a page, or to clean up resources when you leave a page. See Past and future of client-side routing by Router5's author, Thomas Roch

Code conventions

The -ui suffix for function is used to indicate that the function is a Reagent component and should be used in [square-brackets].

Every subpage of the app lives in a separate namesapce in the cljs-spa.page hierarchy. It exposes a page-ui entry point, as well as optional on-activate and on-deactivate hooks.

A page is in one of three states: :loading, :loaded or :failed. The page-state-ui wrapper shows a spinner while loading, and a sad smiley when the on-activate promise failed.

The app relies on higher-order components like layout-ui and router-ui to hide complexity and for better composability.

Testing

Tests use the excellent Extra Main facility. With scripts/dev running, visit http://localhost:9333/figwheel-extra-main/tests to see the cljs-test-display test runner.

The tests in this project are included for demonstration purposes and are expected to fail.

Additionally, this project is set up for automatic testing using a headless browser:

scripts/tests

Highlights

Given that we use webpack, any React component can be used easily. As an example, the Home page includes the excellent react-select component. See code.

Troubleshooting

When you run into surprising behavior in the browser, the first thing to try is to restart the dev env using

scripts/dev --reset

which clears local compilation caches.

Links

This repository is inspired by Richard Feldman's elm-spa-example.

Don't forget to check out Figwheel Main.

License

MIT

Author

Paulus Esterhazy [email protected]

More Repositories

1

vagrant-lamp-ansible

Vagrant starter set to set up PHP, Apache and MySQL
ApacheConf
45
star
2

cljs-corpus

A greppable archive of ClojureScript code
38
star
3

boot-fmt

Boot task to auto-format Clojure(Script) code
Clojure
35
star
4

zprint-mode.el

Emacs minor mode for pretty-printing Clojure using zprint
Emacs Lisp
25
star
5

double-bundle

Example for integrating React NPM dependencies with Clojurescript
Clojure
23
star
6

presumably

presumably for side-effects
TypeScript
19
star
7

recalcitrant

Reagent toolbox for building simple components
Clojure
18
star
8

blissful-bash

Magic spells for Bash
16
star
9

perdure

Clojure's persistent data structures made durable
Clojure
15
star
10

sparkling-example

Clojure
10
star
11

cljs-react-hooks

Demo for React Hooks iin ClojureScript
Clojure
9
star
12

re-natal-husk

Experiment to work around issues with the react native packager
Clojure
9
star
13

boot-react-native-tictactoe

TicTacToe example using boot-react-native
JavaScript
5
star
14

laravel-bench-env

A minimal test environment to benchmark Larvel in a production environment on EC2
PHP
4
star
15

shadow-jest

Shadow-cljs and Jest (experimental)
Clojure
4
star
16

subtitlematch

match subtitle filenames (.srt) to video files (.avi) so that movie players find them
Python
3
star
17

shub

Search Engine Hub, like yubnub
Python
3
star
18

moth

Simple dependency manager
Python
3
star
19

advent2018

Clojure
2
star
20

reagent-playground

Clojure
2
star
21

beep-boop

Audible and visual feedback for test runs
Clojure
2
star
22

untree

Print directory trees like tree(1), from stdin
Clojure
2
star
23

pdf2up

n-up (impose) PDF files (web interface)
PHP
2
star
24

edn2transit

EDN to Transit, and back again
2
star
25

feigenrad

Clojure
1
star
26

lstrasse

Aktuelle Folge der LindenstraรŸe herunterladen
Python
1
star
27

reframe-reloading

Clojure
1
star
28

lein-aot-example

Example to demo use of aot, relatively sanely
Clojure
1
star
29

advent2019

Advent of Code 2019 in Clojure & Typescript
TypeScript
1
star
30

dico2

online dictionary multiplexer
Python
1
star
31

gilded-ts

Gilded Rose kata in TypeScript
TypeScript
1
star
32

accomplice

A logging companion tool
Clojure
1
star
33

blissful-aws

Magic spells for the working AWS magician
JavaScript
1
star
34

ndpr-offprint

Generate nicely formatted PDFs from Notre Dame Philosophical Review (NDPR)
Python
1
star