frontarm / navi Goto Github PK
View Code? Open in Web Editor NEW๐งญ Declarative, asynchronous routing for React.
Home Page: https://frontarm.com/navi/
License: MIT License
๐งญ Declarative, asynchronous routing for React.
Home Page: https://frontarm.com/navi/
License: MIT License
Hi James,
I'm trying to migrate a "large" react application which has modules with it's own routes from react-router v4 to junctions.
I have two questions:
Modules
I currently have an Auth module which has all components like /sign-in and /register. These are "root" routes, but I don't really want them in my root junction. As I find in the documentation, this isn't really possible with junctions because it only has relative paths, and I should OR have it like /auth/sign-in with a auth-junction OR have the routes in the main junction. Is that correct?
:id/edit
I have three routes:
/assets
/assets/:id
/assets/:id/edit
How would I create these in junctions?
Currently I got this far:
const mainJunction = createJunction({
Auth: {
next: AuthScreen.junction,
},
Assets: {
next: AssetsScreen.junction,
}
});
// Junction in AssetsScreen
const junction = createJunction({
Asset: {
path: '/:id',
paramTypes: {
id: { required: true },
},
// idea for /edit
AssetEdit: {
path: '/:id/edit',
paramTypes: {
id: { required: true },
},
});
When I do it like this, I get an error that the routes match.
"Two branches have paths "undefined" and "undefined" that match the same URLs!"
(the undefined is caused by trying to grab .path on this line: https://github.com/jamesknelson/junctions/blob/master/source/createJunction.js#L106)
Is that correct or is that a bug? (I can send a PR if it's a bug)
I also tried /:id(/edit) or /:id/(edit) but none work. Do you think it's possible to have routes like this?
I'm also curious if you have any plans for Junctions. If I'm going to use it I could help you maintain it if you want (and write some more docs).
I also made a Redirect component and a withHistory decorator for react-junctions based on the context. This made it easier to migrate some components from react-router. If you want I could make a PR for them.
Thanks for the library, and looking forward to your reply :)
The Router Challenge aims to be to Routers what TodoMVC is to MV* frameworks. It offers the same Music Catalog application built in React using different Routers. For it to be successful I need the help of Router writers like you. Will you take the Router Challenge and implement the Music Catalog application using Junctions, please?
Example:
For a nested Junction tree with 3 levels:
{
a: { next: createJunction({
b: { next: createJunction({
c: { next: createJunction({ ... }) }
}) }
}) }
}
Locations can be converted to Routes, but running locate
requires that { main: route }
be passed in as an argument, instead of just route
.
I know this has been bought up in #29 but the solution there seems to be outdated. Is there a way to push to a new route as the result of an async request e.g a form submission.
When running npm start
in the site/
directory, babel emits the following errors, and the site fails to load.
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./glossary.md in /junctions/docs
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/SITE.js 6:107-131
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/introduction/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./motivation.md in /junctions/docs/introduction
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/introduction/SITE.js 5:46-72
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./locations.md in /junctions/docs/basics
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js 5:10-35
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./routes.md in /junctions/docs/basics
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js 5:37-59
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./junctions.md in /junctions/docs/basics
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js 5:61-86
ERROR in ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js
Module not found: Error: Cannot resolve 'file' or 'directory' ./links.md in /junctions/docs/basics
@ ../~/babel-loader/lib!../~/babel-loader/lib!../docs/basics/SITE.js 5:172-193
This is due to the case difference between the SITE.js
file lists and the actual .md
files.
SITE.js | Actual filename |
---|---|
./glossary.md |
./Glossary.md |
./motivation.md |
./Motivation.md |
./locations.md |
./Locations.md |
./routes.md |
./Routes.md |
./junctions.md |
./Junctions.md |
./links.md |
./Links.md |
When running navi-scripts build
using hooks it fails with the following error:
Overview
[ohshit] An error occured while building your app
Minified React error #298; visit https://reactjs.org/docs/error-decoder.html?invariant=298 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
Invariant Violation: Minified React error #298; visit https://reactjs.org/docs/error-decoder.html?invariant=298 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
The unminified error message states:
Hooks can only be called inside the body of a function component.
I know hooks are still in alpha but it might be worth investigating this issue before they release.
Steps to reproduce
cd
into navi/examples/create-react-blog
16.7.0-alpha.2
App.js
to use a hook (see below)yarn build
for the exampleHook usage in App.js
export function App(props) {
const [state] = useState(true)
return (
<Nav.Provider navigation={props.navigation}>
<Nav.Loading>
{isLoading => (
<>
<AppBusyIndicator show={isLoading} />
<nav>
<Nav.Link href="/">Home</Nav.Link>
{state ? 'state is true' : 'state is false'}
</nav>
<main>
<Nav.NotFoundBoundary render={renderNotFound}>
<Nav.Route />
</Nav.NotFoundBoundary>
</main>
</>
)}
</Nav.Loading>
</Nav.Provider>
)
}
It would be nice to be able to pass all unrecognized properties form 'NavLink' to generated <a>
tag, like:
<NavLink href="/" data-track="navbar" role="button">
and having data-track
and role
passed to generated anchor:
<a href="/" data-track="navbar" role="button">
Navi doesn't have any ties to React within the navi
package itself. Also, its design is based on the assumption that your View uses components -- but not necessarily React components.
So it should totally be possible to use Navi with Vue.
Need to figure out how to do this and add an example. Would really appreciate help from anyone with some Vue experience.
Hi,
Great work on navi but it would be really helpful to have a changelog to track what is fixed, changed, etc.
Hey there, navi rocks! ^^
My app is dependent on react-native-vector-icons, which seems to fail navi-scripts's build process.
The full error:
Using create-react-app renderer...
Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)
at module.exports (/Users/nickjanssen/Documents/docs/lk-web-app/node_modules/navi-scripts/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at HTMLCanvasElementImpl.getContext (/Users/nickjanssen/Documents/docs/lk-web-app/node_modules/navi-scripts/node_modules/jsdom/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js:42:5)
at HTMLCanvasElement.getContext (/Users/nickjanssen/Documents/docs/lk-web-app/node_modules/navi-scripts/node_modules/jsdom/lib/jsdom/living/generated/HTMLCanvasElement.js:41:47)
As the error indicated, I tried installing canvas
in the root directory as well as in node_modules/navi-scripts
but it doesn't have an effect. Any idea what I could be doing wrong?
Here's a repo with the reproducable error: https://github.com/nickjanssen/navi-test
The documentation site looks pretty bad on mobile. It would be nice to make the following changes:
Also, the font sizes could probably use some adjustment.
Hey,
When clicking same link, it creates duplicates in history so when you click back, you get to the same page.
Tested on basic example.
Click home -> about -> about -> about -> back
. You would expect to go back to home, but you will stay on the same page. Default browser <a>
tag doesn't create same page history duplicates.
This is a well know bug in history package (since v4) which was reported multiple times in react-router (RR#5500, check number of linked issues) and in history (H#507).
React-router guys tell this should be fixed in history while history guy one day tells it should be fixed in react-router, next day says it should fixed in history and wants a pr, next day closes pr with no comments (H#570).
Maybe at least one router will have links that work same way as browser ones.
I'm trying to integrate Junctions into a fresh code base where I'm already using React Router. I'm replicating the raw example and stripped out all the react-router
components to try and get this to work. One tidbit to know is that all of my urls in the entire app need to be prefixed with /wp
to get this to work alongside a legacy codebase.
My issue is that when I go to /wp/about
or /wp/organizations
I just see the word "Home". I dug into it and saw that the route
variable in AppScreen
returns the same object at every one of those locations instead of the corresponding next
junction that it should return.
Here's my code:
import createBrowserHistory from 'history/createBrowserHistory'
import { createJunction, createConverter, locationsEqual } from 'junctions'
AppScreen.junction = createJunction({
wp: {
next: createJunction({
about: {},
help: {},
organizations: {},
}),
},
});
function AppScreen({ route, locate, history }) {
const junction = AppScreen.junction;
let content;
switch (route ? route.key : route) {
case 'wp':
content = <h2>Home</h2>;
break;
case 'about':
content = <h2>About</h2>;
break;
case 'organizations':
content = <h2>Organizations</h2>;
break;
case null:
content = <h2>Home</h2>;
break;
// An undefined route indicates that the converter didn't know how
// to handle the received location
case undefined:
content = <h2>Not Found</h2>;
break;
}
return (
<DefaultLayout
content={content}
history={history}
locate={locate}
route={route}
/>
)
}
class JunctionsRouter extends Component {
componentWillMount() {
// As an application's Junction doesn't usually change at runtime,
// we only ever need a single application-wide converter
this.converter = createConverter(AppScreen.junction)
// Handle the application's initial location
this.handleLocationChange(this.props.history.location)
}
componentDidMount() {
this.unlisten = this.props.history.listen(this.handleLocationChange.bind(this))
}
componentWillUnmount() {
if (this.unlisten) {
this.unlisten()
this.unlisten = null
}
}
handleLocationChange(location) {
// Convert the Location object emitted by our history into a Route
// through our application Junction
const route = this.converter.route(location)
// The Route produced by the converter may contain informatino which
// the received Location object doesn't, due to default parameters
// and default branches. If this is the case, create a new Location
// object containing the new information, and redirect to it
const canonicalLocation = route && this.converter.locate(route)
if (route && !locationsEqual(location, canonicalLocation)) {
this.props.history.replace(canonicalLocation)
}
// Add the route to component state to trigger a re-render
this.setState({ route })
}
render() {
return (
// Screen Components always take `route` and `locate` props, so they
// can decide what to render, and create Locations for any Links
// and redirects.
//
// In this example, we also pass a history. Usually, this would be
// passed via context, using a <Router> or <HistoryContext> component.
<AppScreen
route={this.state.route}
locate={this.converter.locate}
history={this.props.history}
/>
)
}
}
const history = createBrowserHistory();
export default render((
<JunctionsRouter
history={history}
junction={AppScreen.junction}
render={AppScreen}
/>
), document.getElementById('root'));
I really like the idea and simplicity of this library, however I am unable to use it in a TypeScript 2.9 project with strict mode turned on.
Despite setting the 'rootDir' and 'excludes' settings in my TS Config to exclude node_modules
, the typescript compiler still tries to compile the src
files shipped in the npm module, which is failing with a ton of errors (a small subset pasted below).
(I've also tried explicitly setting 'excludes' in my webpack config)
ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts
[tsl] ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts(16,18)
TS2430: Interface 'JunctionRoute<J>' incorrectly extends interface 'Route'.
Types of property '0' are incompatible.
Type 'Junction<J>' is not assignable to type 'RouteSegment'.
Type 'Junction<J>' is not assignable to type 'Junction<JunctionTemplate<any, any, any>>'.
Types of property 'activeRoute' are incompatible.
Type 'JunctionDescendentSegments<J>' is not assignable to type 'JunctionDescendentSegments<JunctionTemplate<any, any, any>>'.
Type 'JunctionTemplate<any, any, any>' is not assignable to type 'J'.
ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts
[tsl] ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts(17,5)
TS2412: Property '0' of type 'Junction<J>' is not assignable to numeric index type 'RouteSegment'.
ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts
[tsl] ERROR in /Users/russell/code/testapp/node_modules/junctions/src/Route.ts(152,5)
TS2412: Property '0' of type '{ [K in keyof J["children"]]: J["children"][K] extends AsyncObjectContainer<infer T> ? { Junction...' is not assignable to numeric index type 'RouteSegment'.
ERROR in /Users/russell/code/testapp/node_modules/junctions/src/PageTemplate.ts
[tsl] ERROR in /Users/russell/code/testapp/node_modules/junctions/src/PageTemplate.ts(3,1)
TS6192: All imports in import declaration are unused.
ERROR in /Users/russell/code/testapp/node_modules/junctions/src/PageTemplate.ts
[tsl] ERROR in /Users/russell/code/testapp/node_modules/junctions/src/PageTemplate.ts(4,10)
TS6133: 'Page' is declared but its value is never read.
My feeling is it would be better to ship the npm package without the source files (just the dist folder with the .d.ts typings), then maybe ship a seperate -src
package for peeps that want to compile the source themselves?
The console throws an error and I am unable to route. The address bar changes but the content remains the same.
I start at
https://junctions.js.org/docs/introduction/do-i-need-a-router
and click a link and the address bar changes to
https://junctions.js.org/docs/introduction/locations-routes-and-maps
but the content never budges.
I get this stacktrace on the bundled js
TypeError: this.refs.html.querySelectorAll(...).forEach is not a function
...
This link works perfectly: https://junctions.js.org/examples/Raw
I am using Firefox 47 on an EOLed Ubuntu. I hope I am helpful enough. I wish your project the best!
Is it possible to dynamically switch to a new route? In my case, I'm doing an API request when user clicks a button. If the request was successful, they are routed to a new page.
Great library! I actually prefer having all the pages is JSON-ish format, plus you get the benefits of react-loadable built in which is perfect.
I was trying this out and wanted to see if there's an example of a route which uses a react class component instead of a function.
So instead of
// pages/reference.js
import * as React from 'react'
export default function Reference() {
return (
<div>
<h2>Reference</h2>
</div>
)
}
What would be the createPage
syntax for this:
import React from 'react'
export default class Reference extends React.Component {
render() {
return <h2>Smart Page!</h2>;
}
}
Thank you!
Website is currently a single-page app, but there really should be HTML pages generated for each of its pages.
In order to do this, the processing on SITE.js files currently handled by the sitepack loader will need to be handled by node itself.
The general idea is:
Is there a way to avoid the "/" characters getting added to my URLs?
In this sandbox, click on the /page1 or /page2 links and you will see the URL change to /page1/ or /page2/. Happens in my own app, too.
https://codesandbox.io/s/n7v5o7j310
All the code:
import React from "react";
import ReactDOM from "react-dom";
import { mount, route } from "navi";
import { Router, Link } from "react-navi";
import "./styles.css";
const routes = mount({
"/": route({
view: () => (
<p>
Home. Go to <Link href="/page1">page 1</Link> or{" "}
<Link href="/page2">page 2</Link>.
</p>
)
}),
"/page1": route({
view: () => (
<p>
Page 1. Go <Link href="/">home</Link> or to{" "}
<Link href="/page2">page 2</Link>.
</p>
)
}),
"/page2": route({
view: () => (
<p>
Page 2. Go <Link href="/">home</Link> or to{" "}
<Link href="/page1">page 1</Link>.
</p>
)
})
});
function App() {
return (
<div className="App">
<Router routes={routes} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Is it possible to disable prefetching everywhere without putting prefetch={false}
on all my links?
Hi! I was impressed by your presentation in Nodefest in this year, and now am trying out react-navi right now to make my new app.
I just would like to know how to go to another path in getContent
. The purpose of this is that I would like to change the rendering according to user login status like below. Can I get navigation
from props or something? Thanks for your help.
export const RootComponent = Navi.createPage({
title: 'index',
getContent: async env => {
const currentUser = await getCurrentUser();
if (!currentUser) {
// Wanna go to `/login` here
}
return component(currentUser)
}
})
const component = (user: User) => {
return (
// ...
)
}
Hi, is there a way to use the navi router with electron?
React-router has the HashRouter, which can be used, navi doesn't seem to have that.
We're using navi in our React-app together with server side rendering. We're working on page speeds and one of our requirements to improve speed is to preload assets such as CSS files and JS files.
To do that, we need to know the set of dynamic modules where rendered in a request. For example, a request to '/' renders two dynamically imported JavaScript bundles and one dynamically imported CSS bundle. Do you have a suggestion for how we can get that information from navi?
We used react-loadable prior to navi, but changed to navi as it solved most of our requirements better with less code so we'd love to keep navi. However, this particular requirement is unclear how to solve using navi. Here's a link to the corresponding react-loadable documentation.
Hello!
I have app with url site.com/some/path/before/AppItself
How to work with url like that?
I tried to specify path in createJunctions as window.basePath + route but I receive message like
Uncaught Error: Branch "branchName" uses a pattern with "anything", but another pattern or alias already uses this identifier.
Thanks
Currently, if the map()
matcher isn't able to match any of its provided paths, it yields a NotFound error segment.
Ideally, it'd be possible to chain multiple map()
matchers, so that not all URLs under a single path must be defined in a single map()
. This should be easy enough to do by using the map()'s child matcher as a fallback (if it exists).
This would be a good first PR, as it's a small change with reasonably simple tests. If you'd like to give it a try, please leave a comment.
I have web app which instead of rendering "not found" page redirects to home page.
Example code: https://frontarm.com/demoboard/?id=1f95147d-e713-4d80-a22a-af880c6810f2
I'm trying to do it this way:
function renderNotFound() {
return (
<NavHistory>{(history) => history.push('/about')}</NavHistory>
)
}
// [...]
<NavNotFoundBoundary render={renderNotFound}>
<NavContent />
</NavNotFoundBoundary>
and it works correctly when my page's getContent()
returns content immediately, but when I try to redirect to async page
'/about': Navi.createPage({
title: 'The createSwitch() function',
getContent: () => import('./about.mdx'),
}),
it fails with following errors in console:
Uncaught Error: URL not found: /abc/
at createNotFoundSegment (VM168 navi.js:322)
at class_1.NodeMatcher.getResult (VM168 navi.js:363)
at class_1.SwitchMatcher.execute (VM168 navi.js:872)
at class_1.NodeMatcher.getResult (VM168 navi.js:356)
at RouteObservable.refresh (VM168 navi.js:1336)
at RouteObservable.handleChange (VM168 navi.js:1321)
at RouteObservable.subscribe (VM168 navi.js:1359)
at CurrentRouteObservable.handleURLChange (VM168 navi.js:1256)
at CurrentRouteObservable.refresh (VM168 navi.js:1180)
at new CurrentRouteObservable (VM168 navi.js:1164)
The above error occurred in the <InnerNavContent> component:
in InnerNavContent (created by Context.Consumer)
in NavContent (created by Context.Consumer)
in InnerNotFoundBoundary (created by Context.Consumer)
in ErrorBoundary (created by Context.Consumer)
in main (created by Context.Consumer)
in div (created by Context.Consumer)
in NavLoading (created by App)
in Suspense (created by NavProvider)
in NavProvider (created by App)
in App
React will try to recreate this component tree from scratch using the error boundary you provided, InnerNotFoundBoundary.
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
at invariant (VM167 react-dom.development.js:49)
at scheduleWork (VM167 react-dom.development.js:18530)
at Object.enqueueSetState (VM167 react-dom.development.js:12437)
at NavProvider.Component.setState (VM166 react.development.js:460)
at Object.NavProvider._this.handleNavigationSnapshot [as next] (VM173 react-navi.js:335)
at MapObserver.next (VM168 navi.js:1963)
at CurrentRouteObservable.handleURLChange (VM168 navi.js:1226)
at VM168 navi.js:1163
at listener (VM171 history.js:878)
at VM171 history.js:897
The above error occurred in the <Context.Consumer> component:
in NavHistory (created by InnerNotFoundBoundary)
in InnerNotFoundBoundary (created by Context.Consumer)
in ErrorBoundary (created by Context.Consumer)
in main (created by Context.Consumer)
in div (created by Context.Consumer)
in NavLoading (created by App)
in Suspense (created by NavProvider)
in NavProvider (created by App)
in App
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
Uncaught (in promise) Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
at invariant (VM167 react-dom.development.js:49)
at scheduleWork (VM167 react-dom.development.js:18530)
at Object.enqueueSetState (VM167 react-dom.development.js:12437)
at NavProvider.Component.setState (VM166 react.development.js:460)
at Object.NavProvider._this.handleNavigationSnapshot [as next] (VM173 react-navi.js:335)
at MapObserver.next (VM168 navi.js:1963)
at CurrentRouteObservable.handleURLChange (VM168 navi.js:1226)
at VM168 navi.js:1163
at listener (VM171 history.js:878)
at VM171 history.js:897
The resolveSiteMap()
function is used by navi-scripts to find a map of all routes during static builds, but in my opinion, it's currently the weakest part of Navi's design. It's heavy, complicated code that isn't frequently used, and it's getting in the way of #60 .
Before 1.0, I want to refactor this so that instead of outputting an object mapping URLs to Route objects, it just outputs a list of URLs (which can then be resolved separately).
The new map function should work by setting the request's method
property to Symbol(map
) instead of the standard GET
method. Then, matchers themselves will be tasked with creating a list of all of their possible children, probably putting them on a new "map" Chunk.
For custom matchers that don't understand the "map" method, they should just treat it as a GET and return a standard route. This shouldn't be a problem, as they'll still pass the request through to their children, which can add more map segments. To facilitate this, it may make sense to remove "GET" as the default, and replace it with undefined
?
Since map is a special method, map configuration can be added to the request body, including predicate/url parameters extraction.
When running navi-scrips serve on a react-navi-app build, it gets stuck for many minutes with no resolution.
# Install the create-react-app command-line tool
npm install -g create-react-app
# Create a new project under the `junctions-tutorial` directory
create-react-app junctions-tutorial
cd junctions-tutorial
# Install junctions and react-junctions
npm install --save junctions react-junctions
# Start a development server at http://localhost:3000
npm run start
you get 'command not found'. Obviously, if you don't add junctions or react-junctions, create-react-app works fine. Noticed that react-junctions hasn't been update in a long while so I'm guessing it just lacks support for the latest react/create-react-app. Should fire off peer dependency warnings IMO to let people know its for react 15 (or whatever it last worked on)
Navi needs some dev tools. As a result of Navi's architecture, they should be able to support time travel and give you a window into what's going on under the hood.
I'd really appreciate any help putting them together. I'd be happy to contribute to and link to a quality project that's maintained by someone else.
When you pass Navi a new URL or Context, it will map them to an array of simple objects called Chunks, which are then reduced into your Route
object with a Redux-like configurable reducer.
The array of chunks corresponding to a single URL/context grows over time. Each Matcher (i.e. mount()
, route()
, withView()
, etc.) maps a URL/context pair to an arbitrary number of chunks to the route during each iteration.
There's one special type of chunk: "busy". When Navi encounters a busy chunk (which will contain a promise), it'll wait until that promise resolves or is rejected, and then it'll recompute the list of chunks from scratch (thus allowing it to grow over time). Once there are no busy chunks, the route is considered "steady".
Due to Navi's architecture, it should be possible to visualize how each Route is computed in response to a Request or Context change, possibly as a list of actions.
Request(/members)
- Chunks [view, busy]
- Chunks [view, redirect(/login), status(302)]
Request(/login)
- Chunks [view, status(200]
Context({ isAuthenticated: true })
- Chunks [view, status(200]
Request(/members)
- Chunks [view, busy]
- Chunks [view, data, busy]
- Chunks [view, data, status(200)]
Being able to see at a glance the information that is mapped to the current URL, and the process used to fetch it, should be useful for debugging routing issues. It would also allow for developers to pick a previous state and use that instead, i.e. time travel.
To support screenreaders, each branch should store a title under data
. Then on navigation, the browser title should be updated, and an announcer div updated.
See https://twitter.com/noopkat/status/818240685715419136
This will need some changes:
It would be nice to have all this built into react-junctions, but I can't see how to make it work:
For now, meta tags is an object and due to hardcoded pattern, end result is always bound to name=""
and content=""
attributes. There is a variety of other attributes, so it seems a must to support this.
It's already possible with custom navi.config.js
, but the main idea is to have more flexible defaults.
Users might like to write their meta this way:
'/': createPage({
title: 'Navi',
meta: [
{
name: 'description',
content: 'navi-description'
},
{
property: 'og:description',
content: 'navi-og-description'
}
]
})
Some kind of draft implementation:
function generateMetaTags(attrs) {
if (!Array.isArray(attrs)) return ''
return attrs
.map(
attr =>
`<meta ${Object.entries(attr)
.map(([key, val]) => `${he.encode(key)}="${he.encode(val)}"`)
.join(' ')}>`
)
.join('')
}
const title = `<title>${route.title || 'Untitled'}</title>`
const meta = generateMetaTags(route.meta)
Navi looks like a really neat little routing alternative. Our data always come from Apollo though which allows to fetch all data required in a whole react tree in server rendering with getDataFromTree
is something like that possible with Navi?
I found this library via a tutorial on Zeit, I'm pretty pleased with the clarity and ease-of implementation so far, so thank you very much!
I recently was hit with this simple and I imagine easy-to-fall-into mistake:
export default createSwitch({
paths: {
"/": createPage({
title: `home`,
getContent: () => import("./Home")
}),
"/test": {
title: "this is a test",
getContent: () => import("./TestPage")
}
})
โ๏ธ So I'm missing a createPage
wrapping on the /test
property.
Because of that, when I try to go to /test
, I see an error:
TypeError: resolvable is not a function
Resolver.resolve
node_modules/navi/dist/es/Resolver.js:54
I'd prefer to see some kind of error like:
Given path "/test" is not a valid path. Provided path should be one of Switch, Page, Redirect or Context. See https://frontarm.com/navi/reference/declarations/#declaring-pages for more info.
I don't have enough context to know whether this would be a reasonable change, but I spend a few minutes tracking this down and it seems like it'd be a common enough mistake for others to run into.
Thanks for your time and thanks for this project -- excited to use it more in the future!
For myself I wrapped the paths property with a map(createPage)
:
import {map} from 'ramda'
export default createSwitch({
paths: map(createPage, { '/': blah, '/test': blah})
})
So that I can avoid that potential footgun, but likely more complicated pages would require more nuance.
I have noticed an issue when using the latest react version 16.8.0-alpha.0
that when i use getContent and do a dynamic import to a page that contains a hook it will blow up with this error:
Hooks can only be called inside the body of a function component.
Has anyone else seen this? And is there a current fix. I have found that I can get around it by using content
and getting the page as a React component but I wonder if thats a great idea as I assume it then pulls in every page on load
Subdomain routing should be possible with Junctions by manually handling events from history, and joining the current subdomain onto the beginning of the pathname.
Unfortunately this will prevent baseLocation support, but I doubt this will be an issue if we're using subdomains. The baseLocation filtering function could be exported and documented if this turns out to be an issue.
Redirects should be able to be specified relative to their mount location using .
and ..
.
This bug is related to the documentation as well as to the dependencies of this library.
It's not stated explicitly what your library's feature coverage is in browsers when comparing using browsers' native window.history vs the history library's API. This makes it unclear what to expect if one uses your library without the history library as a dependency unless the developer tests literally every browser or at least a great many of them.
It would help a fantastic amount if it was stated more explicitly in the docs which precise browsers and features are being shimmed by the history API when/if it's used in a project along with this router. Every little bit of extra code counts, and if it's tenable not to include an extra library, it would help to know that.
Additionally, if it's untenable not to include a given library, maybe because very few browsers based on usage percentage would actually work without that library's support, then it seems that library should be a required dependency.
These are just my opinions. I hope you will consider taking a look at these issues.
Thanks a million.
In scrollToHash function window.scroll
method is called with options object. Unfortunately, this syntax is not supported by IE, Edge, Safari and older versions of Chrome (MDN docs), which only support the old syntax, i.e. window.scroll(x-coord, y-coord)
.
Top 3 JS errors on our site reported by TrackJS are caused by this issue :/ Unfortunately some browsers that don't support it don't throw any error and simply scroll to 0, 0
, but e.g. older versions of Chrome (e.g. 55, even though it should work according to MDN) throw the following error:
Failed to execute 'scroll' on 'Window': No function was found that matched the signature provided.
Hi, I have a index.html file in the public folder, which consists some jquery plugins and what not, navi seems to get stuck while building the application and assumes the jquery plugins are in my build folder, when i'm building the project, although I have defined the script tags.
For example, I'm using script tags like this.
<script src="//code.jquery.com/jquery-3.3.1.min.js"></script>
But when I build, I get an error like
The build folder is ready to be deployed.
You may serve it with a static server:
yarn global add serve
serve -s build
Find out more about deployment here:
http://bit.ly/CRA-deploy
navi-scripts: Using config at C:\Users\Mikk\blog-template\navi.config.js
Error: Could not load script: "//code.jquery.com/jquery-3.3.1.min.js"
at onErrorWrapped (C:\Users\Mikk\blog-template\node_modules\jsdom\lib\jsdom\browser\resources\per-document-resource-loader.js:38:19)
at Object.check (C:\Users\Mikk\blog-template\node_modules\jsdom\lib\jsdom\browser\resources\resource-queue.js:72:23)
at request.then.catch.err (C:\Users\Mikk\blog-template\node_modules\jsdom\lib\jsdom\browser\resources\resource-queue.js:124:14) { [Error: ENOENT: no such file or directory, open 'C:\Users\Mikk\blog-template\build\code.jquery.com\jquery-3.3.1.min.js']
errno: -4058,
code: 'ENOENT',
syscall: 'open',
path:
'C:\\Users\\Mikk\\blog-template\\build\\code.jquery.com\\jquery-3.3.1.min.js' }
[ohshit] An error occured while building your app
ENOENT: no such file or directory, open 'C:\Users\Mikk\blog-template\build\code.jquery.com\jquery-3.3.1.min.js'
Error: ENOENT: no such file or directory, open 'C:\Users\Mikk\blog-template\build\code.jquery.com\jquery-3.3.1.min.js'
error Command failed with exit code 1.
My navi scripts config is below.
import path from 'path'
import getTagsFromSiteMap from './src/utils/getTagsFromSiteMap'
export const renderPageToString = require.resolve('./src/renderPageToString')
export const resolveSiteMapOptions = {
/**
* navi-scripts will call this function when creating a list of URLs which
* need to be statically built. It allows you to substitute in a list of
* values when URLs contain wildcards, e.g. /tags/:tag -> ["/tags/react"]
*/
async expandPattern(pattern, router) {
if (/\/:tag$/.test(pattern)) {
let siteMap = await router.resolveSiteMap('/')
return getTagsFromSiteMap(siteMap).map(tag => pattern.replace(':tag', tag))
}
},
}
/**
* Get the file to write each URL to during the build
*/
export function getPagePathname({ url }) {
if (url.pathname === '/rss/') {
return 'rss.xml'
}
if (url.pathname === '/') {
return 'index.html'
}
return path.join(url.pathname.slice(1), 'index.html')
}
Any ideas what can be causing this?
Hey, I'm really excited about Navi and the potential to replace React Router with it in one of our apps. This is a feature request, and I'm not sure how difficult it would be given Navi's architecture and its use of the history
module, but here we go:
It would be awesome if Navi could bypass history
's automatic decoding of location.pathname
, discussed in this issue here: remix-run/history#505.
That automatic decoding deviates from browser behavior and makes the flow of pathnames very difficult to reason about - it results in divergent behavior when navigating directly to a page via a URL vs when navigating through history with back/forward vs when clicking on a <Link>
. We have worked around this by creating a custom <PageRoute>
component that manually decodes path params from window.location
, but it's all very hack-y.
It would be cool to just drop the entire mess and switch to Navi, however if I'm following the code correctly, I think that bug would still apply here since it's internal to history
. If there's a way for Navi to either provide a workaround that doesn't depend on history
for the path params, or for Navi to drop the dependency on history
entirely (like Reach Router does), that would be really awesome!
I have been trying to import navi directly from unpkg and have come across a few errors:
https://unpkg.com/[email protected]?module
then this error appears:Apparently this is related to webpack webpack/webpack#8838
https://unpkg.com/[email protected]/dist/es/index.js
then the above error disappears but then this error appears:Apparently this is to do with navi having history
as an optional dependency (for interoperability with react-router). Solving this issue is likely to negate issue 1.
Good day!
I have an url like:
site.com/thing?version=5
where thing is Route
I need get parameter to handle request correctly on server. But it's vanished after router initialisation. How to keep it?
I can't seem to find this in the docs, but is it possible to route with a '*' wildcard that acts as a middleware?
Example:
const routes = mount({
'/login': route({
view: <div>Login</div>,
}),
'/': redirect('/login'),
'*': map((req, { user }) => (user ? protectedRoutes : redirect(req.originalUrl))),
});
SideEffect
is causing an error in StrictMode
.
SideEffect.prototype.componentWillMount = function componentWillMount() {
mountedInstances.push(this);
emitChange();
};
found this in react-navi/dist/umd/react-navi.js
. Using StrictMode
it causes this error:
Warning: Unsafe lifecycle methods were found within a strict-mode tree:
in StrictMode (at App.tsx:7)
in App (at src/index.tsx:6)
componentWillMount: Please update the following components to use componentDidMount instead: SideEffect(NullComponent)
Learn more about this warning here:
https://fb.me/react-strict-mode-warnings
Is there a recommend way of handling authorized URLs with Junctions?
First off, thanks for creating this! Super exciting project ๐ Currently migrating a large web app from react-router
to navi
.
It seems to be an Uncaught Error: URL not found
when navigating to a page that doesn't exist. From the docs I believe the error is supposed to be a NotFoundError
so that the <NavNotFoundBoundary />
but it just looks like a generic error based on the console output. The strange thing is that the Error boundary catches the error ๐ค
navi: 0.10.5
react-navi: 0.10.6
react: 16.6.0
Because the error message is not present on my Express server (using createMemoryNavigation
) it leads me to believe the problem might be related to createBrowserNavigation
, but I'm uncertain.
The error is also present on the examples on the Navi docs webpage ๐
Errors.js:24
Uncaught Error: URL not found: /asdf/
at createNotFoundSegment (Segments.js:38)
at class_1../node_modules/navi/dist/es/Node.js.NodeMatcher.getResult (Node.js:29)
at class_1../node_modules/navi/dist/es/Switch.js.SwitchMatcher.execute (Switch.js:121)
at class_1../node_modules/navi/dist/es/Node.js.NodeMatcher.getResult (Node.js:22)
at RouteObservable.refresh (RouteObservable.js:28)
at RouteObservable.handleChange (RouteObservable.js:13)
at RouteObservable../node_modules/navi/dist/es/RouteObservable.js.RouteObservable.subscribe (RouteObservable.js:51)
at CurrentRouteObservable../node_modules/navi/dist/es/CurrentRouteObservable.js.CurrentRouteObservable.handleURLChange (CurrentRouteObservable.js:186)
at CurrentRouteObservable../node_modules/navi/dist/es/CurrentRouteObservable.js.CurrentRouteObservable.refresh (CurrentRouteObservable.js:110)
at new CurrentRouteObservable (CurrentRouteObservable.js:94)
NaviError @ Errors.js:24
NotFoundError @ Errors.js:40
createNotFoundSegment @ Segments.js:38
./node_modules/navi/dist/es/Node.js.NodeMatcher.getResult @ Node.js:29
./node_modules/navi/dist/es/Switch.js.SwitchMatcher.execute @ Switch.js:121
./node_modules/navi/dist/es/Node.js.NodeMatcher.getResult @ Node.js:22
RouteObservable.refresh @ RouteObservable.js:28
RouteObservable.handleChange @ RouteObservable.js:13
./node_modules/navi/dist/es/RouteObservable.js.RouteObservable.subscribe @ RouteObservable.js:51
./node_modules/navi/dist/es/CurrentRouteObservable.js.CurrentRouteObservable.handleURLChange @ CurrentRouteObservable.js:186
./node_modules/navi/dist/es/CurrentRouteObservable.js.CurrentRouteObservable.refresh @ CurrentRouteObservable.js:110
CurrentRouteObservable @ CurrentRouteObservable.js:94
createCurrentRouteObservable @ CurrentRouteObservable.js:53
BrowserNavigation @ BrowserNavigation.js:92
createBrowserNavigation @ BrowserNavigation.js:44
main$ @ index.js:27
tryCatch @ runtime.js:65
invoke @ runtime.js:303
prototype.(anonymous function) @ runtime.js:117
tryCatch @ runtime.js:65
invoke @ runtime.js:155
(anonymous) @ runtime.js:202
callInvokeWithMethodAndArg @ runtime.js:201
enqueue @ runtime.js:224
prototype.(anonymous function) @ runtime.js:117
./node_modules/regenerator-runtime/runtime.js.runtime.async @ runtime.js:248
main @ index.js:26
./src/index.js @ index.js:72
__webpack_require__ @ bootstrap:68
0 @ commons.js:91567
__webpack_require__ @ bootstrap:68
(anonymous) @ bootstrap:238
(anonymous) @ bootstrap:238
Promise.then (async)
invoke @ runtime.js:164
(anonymous) @ runtime.js:202
callInvokeWithMethodAndArg @ runtime.js:201
enqueue @ runtime.js:224
prototype.(anonymous function) @ runtime.js:117
./node_modules/regenerator-runtime/runtime.js.runtime.async @ runtime.js:248
main @ index.js:26
./src/index.js @ index.js:72
__webpack_require__ @ bootstrap:68
0 @ commons.js:91567
__webpack_require__ @ bootstrap:68
(anonymous) @ bootstrap:238
(anonymous) @ bootstrap:238
react-dom.development.js:15123
The above error occurred in the <InnerNavContent> component:
in InnerNavContent (created by Context.Consumer)
in NavContent
in InnerNotFoundBoundary (created by Context.Consumer)
in ErrorBoundary (created by Root)
in NavProvider (created by Root)
in IntlProvider (created by LanguageProvider)
in LanguageProvider (created by Root)
in Provider (created by Root)
in Root
React will try to recreate this component tree from scratch using the error boundary you provided, InnerNotFoundBoundary.
logCapturedError @ react-dom.development.js:15123
logError @ react-dom.development.js:15157
callback @ react-dom.development.js:15931
callCallback @ react-dom.development.js:11194
commitUpdateEffects @ react-dom.development.js:11233
commitUpdateQueue @ react-dom.development.js:11224
commitLifeCycles @ react-dom.development.js:15271
commitAllLifeCycles @ react-dom.development.js:16523
callCallback @ react-dom.development.js:149
invokeGuardedCallbackDev @ react-dom.development.js:199
invokeGuardedCallback @ react-dom.development.js:256
commitRoot @ react-dom.development.js:16677
completeRoot @ react-dom.development.js:18069
performWorkOnRoot @ react-dom.development.js:17997
performWork @ react-dom.development.js:17901
performSyncWork @ react-dom.development.js:17873
requestWork @ react-dom.development.js:17761
scheduleWork @ react-dom.development.js:17566
scheduleRootUpdate @ react-dom.development.js:18240
updateContainerAtExpirationTime @ react-dom.development.js:18267
updateContainer @ react-dom.development.js:18324
./node_modules/react-dom/cjs/react-dom.development.js.ReactRoot.render @ react-dom.development.js:18586
(anonymous) @ react-dom.development.js:18726
unbatchedUpdates @ react-dom.development.js:18124
legacyRenderSubtreeIntoContainer @ react-dom.development.js:18722
render @ react-dom.development.js:18783
main$ @ index.js:64
tryCatch @ runtime.js:65
invoke @ runtime.js:303
prototype.(anonymous function) @ runtime.js:117
tryCatch @ runtime.js:65
invoke @ runtime.js:155
(anonymous) @ runtime.js:165
Promise.then (async)
invoke @ runtime.js:164
(anonymous) @ runtime.js:202
callInvokeWithMethodAndArg @ runtime.js:201
enqueue @ runtime.js:224
prototype.(anonymous function) @ runtime.js:117
./node_modules/regenerator-runtime/runtime.js.runtime.async @ runtime.js:248
main @ index.js:26
./src/index.js @ index.js:72
__webpack_require__ @ bootstrap:68
0 @ commons.js:91567
__webpack_require__ @ bootstrap:68
(anonymous) @ bootstrap:238
(anonymous) @ bootstrap:238
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.