A tiny history API with nested contexts.
This is being iterated on quite often, expect breaking changes for a while.
Check out this example on flems
npm install superhistory@next
import superhistory from 'superhistory'
const A = superhistory()
A.go('/a/b/c/d')
window.location.pathname
// '/a/b/c/d'
A.get()
// { path: 'a/b/c/d' }
A.go('/a/b/c/d/e')
A.go('/a/b/c/d/e/f', { replace: true })
A.get()
// { path: '/a/b/c/d/e/f', localPath: '/a/b/c/d/e/f' }
A.back()
A.get()
// { path: '/a/b/c/d', localPath: '/a/b/c/d' }
const B = A.get('/a/b')
const C = B.get('/c')
B.get()
// { localPath: '/c/d', path: '/a/b/c/d' }
C.get()
// { localPath: '/d', path: '/a/b/c/d' }
C.go('/g')
;[C.get(), B.get(), A.get()]
// [
// { localPath: '/g', path: '/a/b/c/g' },
// { localPath: '/c/g', path: '/a/b/c/g' } ,
// { localPath: '/a/b/c/g', path: '/a/b/c/g' }
// ]
C.preview('/h')
// 'a/b/c/h'
C.get()
// { localPath: '/a/b/c/g', path: '/a/b/c/g' }
superhistory(
options?: {
_window?: Window,
onChange?: (state: superhistory.State) => void
}
): superhistory.Instance
Creates a superhistory instance. You can optionally pass in onChange
to be notified any time a path is set, or popstate
fires
The _window
parameter there is available as an override for serverside usage or tests. For example you could use JSDOM.
type State = {
path: string | undefined,
localPath: string | undefined
}
instance.get(): State
Returns the instance's localPath
in its local context and the global path
. Returns undefined
for both properties if a nested history is not compatible with the current browser location pathname.
Shows what the browser location path will be were you call instance.go(path)
.
Useful for generating
href
's for anchor tags.
instance.go(path: string, options?: { replace: boolean })
A simple wrapper around history.pushState
/ history.replaceState
that works with nested contexts.
instance.back(): void
A simple wrapper around history.back()
instance.end(): void
For the root node, this removes the listener for the popstate
event and clears the list of children
Removes a child instance and any descendents from the list of child nodes used for notifying onChange
callbacks. This will allow it to be gc'd but will not prevent you from using a child node as a getter/setter if it is still in scope.
instance.prefix(): string
Returns the complete normalized prefix of the child instance (including all parent prefixes). For the sake of polymorphism, the root node returns '/'
despite not having a prefix.
instance.child(options: { prefix: string, onChange?: (state: State) => void })
Create a nested istory context. All history operations will receive/return a local pathname that does not include the prefix
that you supply.
These operations are applied directly to the history API by the child instance using the concatenated prefixes of all of its parents.
You can also optionally provide an onChange callback which will notify you when a route is set or on onpopstate
.
Note there is no diffing for onChange
callbacks, if any route instance updates all route instances get notified that the URL is being changed.
superhistory.joinPath(a: string, b: string): string
The function used internally to combine any two paths.
superhistory.normalizePath(path:string): string
The function used internally to normalize the formatting of paths.
This library isn't a router, its just a small wrapper around the browser history API that you could build a pattern matching router on top of.
If you're looking for a pattern matching router engine: Check out superouter
While not strictly correct, superhistory removes trailing slashes as applications tend to treat /a/b/c
and /a/b/c/
as equivalent. By normalizing paths in this way the internal logic becomes much simpler when gluing together prefixes on nested child nodes.
In addition to removing trailing slashes, superhistory also converts empty strings (''
) into single slashes ('/'
). This behaviour ensures all paths and prefixes start with a slash which again simplifies concatenation.