npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

raj-spa

v0.0.14

Published

Single Page Apps for Raj

Downloads

8

Readme

Raj SPA

Single Page Apps for Raj

npm install raj-spa

npm Build Status Greenkeeper badge

Usage

import spa from 'raj-spa'
import {program} from 'raj-react'
import React from 'react'

import router from './router'
import homepage from './pages/home'

function getRouteProgram (route) {
  if (route === '/') {
    // Static program routing
    return homepage
  }

  if (route.startsWith('/users/')) {
    // Dynamic, code-split routing (using ES6 import())
    // i.e. you can return a promise which resolves a program
    const userId = route.split('/').pop()
    return System.import('./pages/user.js').then(page => page.default(userId))
  }

  // 404
  return System.import('./pages/not-found.js')
}

const initialProgram = {
  init: [],
  update: () => [],
  view () {
    return <p>Loading application...</p>
  }
}

export default program(React.Component, () => spa({
  router,
  getRouteProgram,
  initialProgram
}))

Documentation

The raj-spa package exports a single function which takes the following arguments and returns a RajProgram configuration which can plug into raj-react or another Raj program.

Required configuration

| Property | Type | Description | | -------- | ---- | ----------- | | router | RajRouter | The router to which the SPA will subscribe. | getRouteProgram | route => RajProgram | The mapping from routes to programs which receives a route and returns a RajProgram or a Promise that resolves a RajProgram. | initialProgram | RajProgram | The initial program used before the first received route from the router resolves. The transition to the first route's program should be instantaneous if a static program returns from getRouteProgram.

Optional configuration

| Property | Type | Description | | -------- | ---- | ----------- | | errorProgram | Error => RajProgram | The program used when loading a program rejects with an error. errorProgram receives the error and returns a RajProgram. | viewContainer | function | A container view which wraps the entire application. The function will receive a ViewContainerModel and the sub program's view result to encapsulate.

Types

RajProgram

This is a normal Raj program.

interface RajProgram {
  init: [model, effect];
  update: function(msg, state);
  view: function(state, dispatch);
}
RajRouter

Raj SPA will work with any router that is compatible with the following interface. Note that Raj Spa makes no assumption about the underlying navigation rules. Choices, such as push-state or hash-state, are all encapsulated by the router.

interface RajRouter {
  subscribe (): {
    effect: (dispatch: (message: any) => void) => void,
    cancel: () => void
  };
}

The effect method is an effect so it will receive dispatch which it will call with dispatch(route) at the appropriate times.

The cancel method cancels the subscription to route changes. When the cancel function calls, route changes should stop dispatching.

Note: route can be anything that your getRouteProgram understands.

ViewContainerModel

The viewContainer function receives this object.

interface ViewContainerModel {
  isTransitioning: boolean;
}

Keyed programs and nested SPAs

By default, every emitted route causes the teardown of the current program and the set up of the next program. For a minor change in query params or a major change in pages that share programs(s), this can lose useful state and thrash the view. To preserve a program between route changes, we wrap that program in keyed.

function getRouteProgram (route, { keyed }) {
  if (route === '/simple-page') {
    return simpleProgram
  }
  if (route.startsWith('/nested-spa')) {
    return keyed('my-key', router => nestedSpa(router))
  }
}

The keyed function takes a key and function to call to get the program. The key can be any truthy value, which will be compared with the previous key (or lack thereof) using === strict equality. If the key is the same between getRouteProgram calls, the program is preserved. In order to respond to route changes within a keyed program, the keyed function is passed a RajRouter which emits routes and can be subscribed to by the program.

Questions

Does this work with React Native?

Yes, Raj SPA is unaware of any particular URL-based routing. You can define a "headless" router that emits routes you pick.

Server-side rendering?

If you are using React, the raj-react bindings return a React Component that works with ReactDOMServer to get started.

More advanced/experimental strategies exist to increase performance and content delivery, but I won't give advice until I try them out myself.

Are there any routers I can steal?

Here is a hash-change router implementation.

export default {
  subscribe () {
    let listener
    return {
      effect (dispatch) {
        listener = () => dispatch(window.location.hash)
        window.addEventListener('hashchange', listener)
        listener() // dispatch initial route
      },
      cancel () {
        window.removeEventListener('hashchange', listener)
      }
    }
  }
}

I recommend using something on top of this like tagmeme to define and then pattern match on different pages.

My pages have the same header/footer...?

Use containerView to describe all the universal parts of your app.

export function containerView (viewContainerModel, subView) {
  return <div>
    <header>...</header>
    <main>{subView}</main>
    <footer>...</footer>
  </div>
}

Or, you can nest the SPA component in a surrounding program/component.