kriasoft / universal-router Goto Github PK
View Code? Open in Web Editor NEWA simple middleware-style router for isomorphic JavaScript web apps
Home Page: https://www.kriasoft.com/universal-router/
License: MIT License
A simple middleware-style router for isomorphic JavaScript web apps
Home Page: https://www.kriasoft.com/universal-router/
License: MIT License
Hi Konstantin,
Could the docs for react-routing be outdated? The README file shows examples of router.use(...)
and router.route(...)
, but those don't seem to work or exist in the source code.
Thanks!
Bruno
Does universal-router plan to support query strings and hashes? From my tests, it appears that these are not currently supported.
For example:
myapp/test?foo=1&bar=5#hash
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
http://www.kriasoft.com/react-routing
/docs/index.html - site layout based on lodash.template
/docs/css/*.css - CSS4 styles based on PostCSS, cssnext
/docs/*.md - site pages (markdown with highlight.js support)
npm start # starts a lightweight development server, to run the documentation site locally
Compilation logic for it is located in /tools/serve.js
.md
file to index.html
(see tools/serve.js)How would you do this ? to make sure that the url is authorized
**Update:
Answer from @koistya on Gitter:
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/26405822-redirect-route-authorized-routes?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).One way to do so is to have a route handler similar to this one const authorize = (state, next) => { if (!state.user) { state.redirect = '/login'; next(); } } and then in your routes file:
on('/admin/users', authorize, (state) => )
And add a handler in your render function which checks if state.redirect was set, and if so redirect user to the new location by using Location.pushState(null, state.redirect) on a client, and req.redirect(state.redirect) on server
Hi @koistya ! I've been playing with the kit these days and I've found one interesting detail that might bring to your attention.
It's all about the 'isomorphic' server side rendering.
When the user initially loads the app, the whole app is shipped through an HTML file which is being rendered lightning fast. However, when you press F12 and check the network flow then you will see that another API call for the identical URL is made for the currently loaded web page. Suddenly I noticed that app.js file does not know about the fact that the page has already been rendered by the server! Wouldn't it be sweet to add some property to app.js like 'serverRendered' and check it in run method? ;p
document.title...
because webpack will bundle all page .js together,
but not every page have to all the things.
so.
can you provide a webpack plugin to do this.
thx
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
I am trying to think of a way to do redirection.
Here are some examples, of which one requires dispatch to return promise.
// Redirect / to /home
{on("/", async() => {
const {state, component} = await this.dispatch("/home");
return component;
})}
Or provide a new api redirect
(same level as on
)?
redirct("/", "/home");
Apparently second one is better. Maybe adding this will be good?
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/31733774-url-redirect-dispatch-to-return-promise?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).// Let's say we have these 2 routes:
// `/home/:myValueA` route
async action(args) {
args.context.store.dispatch({type: 'onRouteEnter', params: args.params});// or
return (
<Home />
);
},
// `/other/:myValueB` route
async action(args) {
args.context.store.dispatch({type: 'onRouteEnter', params: args.params});
return (
<Other />
);
},
// Let's say we are in the `/home` route
// and we navigate to the `/other` route.
// The problem is that when it enters the `/other` route it dispatches the `onRouteEnter` action;
// The reducer will save the `params` in the store;
// Now the `<Home/>` component is connected to the store so it will render again and
// will get the new `params` value; <-- this is the problem, because sometimes is not what we want.
// After that the `<Other />` component is rendered and will use the new `params`;
// Question: How do you solve this issue? How do you make sure `<Home/>` component is not called with
// the `params` of the `/other` route?
One of the reason that I would prefer to keep it in the store is because you can use time travel with redux devtools. If you do like in your example, the url doesn't change when the you go back in the redux devtools timeline.
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/38300428-question-save-url-params-in-redux-store-and-don-t-render-previous-connected-route-with-the-new-params?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).I have used react-routing in my react app (via react-starter-kit). It works fine on all browsers except for Chrome on iOS. The root url keeps on reloading. I was looking for possible causes and stumbled across this thread on react-router. There was some issue with history api. I am guessing a similar issue for react-routing. I din't find a way to debug chrome on ios. Can anyone help?
Hi all,
First of all, great work on this project. I like how universal-router
isn't tightly coupled to the browser history/navigation code, making it a very versatile routing library.
Speaking of which, it seems that the recommended way to navigate and respond to location changes is to pair universal-router
+ history
, as shown in #72 and in React Starter Kit.
This generally works well — however, it doesn't fit into the Redux architecture and thus breaks the devtools experience (time-travel debugging, etc).
I'd like to share an alternative, "Redux-first" method for achieving navigation and location listening. It allows:
This is achievable with just a small amount of boilerplate code (Redux middleware, reducer, and action creators), which I've packaged into a library: redux-first-routing
.
Here's what you get on the state tree:
{
..., // other redux state
router: {
pathname: '/nested/path/',
search: '?with=query',
queries: {
with: 'query',
},
hash: '#and-hash'
}
}
Together, universal-router
+ redux-first-routing
can be used to create a complete Redux routing solution for any front-end framework/library:
You can find a basic code recipe and live demo here:
And you can apply the same techniques to a larger application, like React Starter Kit's feature/redux
branch.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
I am having hard time to figure out how to setup a full working react
app. Are there any examples out there?
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
without reloading the page? there are tricks like transitionTo and history in react-router; do these work with react-routing as well?
Dave
# Error Message:
node_modules\regenerator-runtime\runtime-module.js (5:303)
The 'this' keyword is equivalent to 'undefined' at the top level of an ES module,
and has been rewritten.
// app.js
import { resolve } from 'universal-router';
const routes = [
{ path: '/', action: () => '<h1>Page One</h1>' },
{ path: '/two', action: () => '<h1>Page Two</h1>' },
{ path: '*', action: () => '<h1>Not Found</h1>' }
];
resolve(routes, { path: '/' }).then(result => {
document.body.innerHTML = result;
});
resolve(routes, { path: '/two' }).then(result => {
document.body.innerHTML = result;
});
resolve(routes, { path: '*' }).then(result => {
document.body.innerHTML = result;
});
// Can I use `Promise.all`?
[...]
return rollup({
entry: path.join(SOURCE_ROOT, 'app.js'),
format: 'iife',
plugins: [
globals(),
builtins(),
resolve({ jsnext: true, browser: true }),
commonjs(),
babel()
]
})
[...]
resolve.js line 31:
const newContext = Object.assign({}, context, value);
So I've been banging my head on this one.
Using react-intl, how can I go from
export default {
path: '/myurl',
to
export default {
path: formatMessage({ id: route.myurl }),
Thanks! #
Hi koistya, I'm newbie in React JS, I'm using SB Admin React Starter Kit to create my app, I wish to create kind REST routes :
http://example.com/users
http://example.com/users/1
http://example.com/users/create
http://example.com/users/add
…
But I can't figure out how to make it, may you can help me to solve my problem ?
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/38907533-how-to-create-rest-routes-on-sb-admin-react-starter-kit?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).What do you think about renaming match()
method to resolve()
? This method doesn't only find the route matching the given URL path, but also executes its action method and returns result to the caller.
What is best practice for having multiple routes point to the same canonical page? E.g.:
Having both routes work is better UX for the user, and both pages will always display exactly the same information.
One option is something like the following, although it risks typos and missed updates if the page component props change. Even better would be specifying multiple paths in a single route object. Is there any way of doing this or another way of generating multiple routes that point to a single canonical URL?
const canonicalRelPath = '/autos/automatic/diesel';
const alternateRelPath = '/autos/diesel/automatic';
export default {
path: canonicalRelPath,
action() {
return <AutomaticDieselLandingPage canonicalPath={url.resolve(base_url, canonicalRelPath)}/>;
},
};
export const alternatePath = {
path: alternateRelPath,
action() {
return <AutomaticDieselLandingPage canonicalPath={url.resolve(base_url, canonicalRelPath)}/>;
},
};
For example i have route "/search" and 100 parameters in app logic, how to pass this in "state"?
Just for process routes like:
/search?form[a]=1&form[b]=1&form[c]=4
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
This is the currently lib/Match.js
file that is pulled down during npm install
,
/**
* React Routing | http://www.kriasoft.com/react-routing
* Copyright (c) Konstantin Tarkus <[email protected]> | The MIT License
*/
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var Match = function Match(route, path, match) {
_classCallCheck(this, Match);
this.route = route;
this.path = path;
this.match = match;
};
exports["default"] = Match;
module.exports = exports["default"];
As of 0.0.4 it should be:
/**
* React Routing | http://www.kriasoft.com/react-routing
* Copyright (c) Konstantin Tarkus <[email protected]> | The MIT License
*/
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var Match = function Match(route, path, keys, match) {
_classCallCheck(this, Match);
this.route = route;
this.path = path;
this.params = Object.create(null);
for (var i = 1; i < match.length; i++) {
this.params[keys[i - 1].name] = decodeParam(match[i]);
}
};
function decodeParam(val) {
if (!(typeof val === 'string' || val instanceof String)) {
return val;
}
try {
return decodeURIComponent(val);
} catch (e) {
var err = new TypeError('Failed to decode param \'' + val + '\'');
err.status = 400;
throw err;
}
}
exports['default'] = Match;
module.exports = exports['default'];
Hi, I just wanted to let you guys know that I love Universal Router. I think it's a brilliant piece of code!
I ended up forking it though, because I didn't want to include babel-runtime and babel-regenerator-runtime in my web bundle. I released the fork as uroute. Maybe it can be of use to others that can't / don't want to depend on generator functions.
Or is there a decent way to do several things in resolveRoute?
e.g.
For example, how would I redirect to an external site, such as http://google.com
The reason I ask, my application site is a different site than my marketing public site, so when a user signs out, i actually want to redirect them to a url not in the application
Lets say I've received an 404 error in Universal router . And I'm trying to debug the Url that caused the error, I cant see the url as while creating the error we are not passing the URL
I'm wondering if anyone with experience using this knows any of the pros/cons?
Hey!
@koistya if possible can you tell me how can I get a parameter, in this case the language locale, in the app.js file (the entrance file)?
Thanks!
Hi, in my project I along with route path like /welcome
, /sign_in
(specific string matched) I have also path /:id
. I've found only way to match this route is to place it on top of routes
so it looks like:
const routes = [
{ path: '/:id', name: 'idRoute' },
{ path: '/welcome', name: 'welcome' },
{ path: '/sign_in', name: 'signIn' }
];
And it seems kind of strange for routes to be resolved bottom->top, wouldn't it make more sense to go top->bottom? So it checks all routes first and if none matches then it comes to /:id
?
Any plans to add named routes? Hardcoding hrefs feels very permanent. Named routes allow you to completely separate URL structure logic from your views.
I understand that you're trying to keep this lightweight -- however I feel having named routes and a transition function that takes the parameters as args helps keep all URL related logic in the router.
Otherwise, URL changes mean switching out hrefs in the views.
Thoughts?
Using the react-starterkit setup and have added a new route /authenticate. When I post to the route, I get a response "Cannot POST /authenticate". However, it will happily route to it via a GET.
for example:
on('/authenticate', async () => <div>hello</div>);
will error with a POST, but return the element with a GET. Does the router support POST operations or am I doing something very wrong?
I have a route that has an :id parameter and also contains a bunch of children. The children of this route need access to the :id parameter but right now the only way to get it is to parse context.baseUrl.
There should be a way to access the params from the parent route in each of the children.
I'd like to have breadcrumbs for my site. Does anyone have recommendations for implementing them as part of universal-router? Thanks.
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/36212127-example-of-breadcrumbs?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).If I have children routes, how do I use parent route's component as a container for all children routes to render in?
For example:
path: '/auth',
component: <Auth title={title} />,
children: [
{
path: '/login',
action: () => {
return {
title,
component: <Login title={title} />,
};
},
},
{
path: '/register',
action: () => {
return {
title,
component: <Register title={title} />,
};
},
},
And then in Auth component, I have {this.props.children}
where children component should be rendered at. Is there a way to achieve this in Universal Router?
Hi there. First off, your code is fantastic. I'm loving working with it.
Second, I'm trying to build a default handler for the browser back button. My thought was to have the router have a history array and push the states onto it (with a cap of course) and then if the user pushes the back button I could use the handlePopState event handler, pop the last state off and reapply it.
I have this all coded up but have a few questions:
npm link react-routing
. When I do, even after doing npm run-script build
it gives me this error:/Users/jjung/dev/web/jd-demos/build/webpack:/src/router.js:4
import Router from 'react-routing/src/Router';
^
Error: Cannot find module "react-routing/src/Router"
at webpackMissingModule (/Users/jjung/dev/web/jd-demos/build/webpack:/src/router.js:4:20)
at Object.module.exports.Object.defineProperty.value (/Users/jjung/dev/web/jd- demos/build/webpack:/src/router.js:4:20)
at __webpack_require__ (/Users/jjung/dev/web/jd-demos/build/webpack:/webpack/bootstrap 14ccd3919092f1554a9b:19:1)
at Object.<anonymous> (/Users/jjung/dev/web/jd-demos/build/webpack:/src/server.js:9:40)
at __webpack_require__ (/Users/jjung/dev/web/jd-demos/build/webpack:/webpack/bootstrap 14ccd3919092f1554a9b:19:1)
at module.exports._this2 (/Users/jjung/dev/web/jd-demos/build/webpack:/webpack/bootstrap 14ccd3919092f1554a9b:39:1)
at Object.<anonymous> (/Users/jjung/dev/web/jd-demos/build/webpack:/webpack/bootstrap 14ccd3919092f1554a9b:39:1)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)`
I have a route (/client/:id) that points to a component Client. In Client I want to take the ID from context.params and use it as a parameter for my Apollo query.
Apollo has a HOC called graphql that has an "options" parameter. options can be a function that takes in the component props and allows you to transfer the props to the Apollo query. I don't see any way to access the context from within that options function.
Has anybody else run into this issue? Is there a way inside of react to automatically transfer the context to a parameter, or is there a way to do this with Universal Router?
I'm getting the following error when I try to generate a url for a nested route.
router.root.children is getting logged as follows:
[ { path: '/:division', name: 'division', action: [Function: action], children: [ undefined ], parent: { path: '/', name: 'root', children: [Circular], action: [Function: action], parent: null } } ]
I'm not sure why children: is showing up as [ undefined ]
as actual routing is working fine. My issue is specifically related to url generation.
Stack trace is as follows:
TypeError: Cannot set property 'parent' of undefined at update (/var/www/node_modules/src/generateUrls.js:27:9) at update (/var/www/node_modules/universal-router/generateUrls/main.js:30:9) at UrlService.url (/var/www/node_modules/universal-router/generateUrls/main.js:39:7) at UrlService.getUrl (/var/www/build/webpack:/service/UrlService.js:17:17) at Login.render (/var/www/build/webpack:/routes/client/auth/default/Login.js:46:57) at /var/www/node_modules/react/lib/ReactCompositeComponent.js:793:21 at measureLifeCyclePerf (/var/www/node_modules/react/lib/ReactCompositeComponent.js:74:12) at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (/var/www/node_modules/react/lib/ReactCompositeComponent.js:792:27) at ReactCompositeComponentWrapper._renderValidatedComponent (/var/www/node_modules/react/lib/ReactCompositeComponent.js:819:34) at ReactCompositeComponentWrapper.performInitialMount (/var/www/node_modules/react/lib/ReactCompositeComponent.js:361:30) at ReactCompositeComponentWrapper.mountComponent (/var/www/node_modules/react/lib/ReactCompositeComponent.js:257:21) at Object.mountComponent (/var/www/node_modules/react/lib/ReactReconciler.js:47:35) at ReactCompositeComponentWrapper.performInitialMount (/var/www/node_modules/react/lib/ReactCompositeComponent.js:370:34) at ReactCompositeComponentWrapper.mountComponent (/var/www/node_modules/react/lib/ReactCompositeComponent.js:257:21) at Object.mountComponent (/var/www/node_modules/react/lib/ReactReconciler.js:47:35) at /var/www/node_modules/react/lib/ReactServerRendering.js:46:36 at ReactServerRenderingTransaction.perform (/var/www/node_modules/react/lib/Transaction.js:138:20) at renderToStringImpl (/var/www/node_modules/react/lib/ReactServerRendering.js:44:24) at Object.renderToString (/var/www/node_modules/react/lib/ReactServerRendering.js:74:10) at _callee$ (/var/www/build/webpack:/server.js:179:30) at tryCatch (/var/www/node_modules/regenerator-runtime/runtime.js:63:40) at GeneratorFunctionPrototype.invoke [as _invoke] (/var/www/node_modules/regenerator-runtime/runtime.js:337:22) at GeneratorFunctionPrototype.prototype.(anonymous function) [as next] (/var/www/node_modules/regenerator-runtime/runtime.js:96:21) at step (/var/www/node_modules/babel-runtime/helpers/asyncToGenerator.js:17:30) at /var/www/node_modules/babel-runtime/helpers/asyncToGenerator.js:28:13 at process._tickDomainCallback (internal/process/next_tick.js:129:7)
hi,when i use it ie8,thorw one error:
Unhandled promise rejection TypeError: Object does not support the "next" property or method.
async function next() {
if (({ value, done } = handlers.next()) && !done) {
const [match, handler] = value;
state.params = match.params;
return handler.length > 1 ?
await handler(state, next) : await handler(state);
}
}
Hi guys!
Thanks for the awesome work!
I want to suggest a feature: full support of query params. Query params should be first-class citizens of a router. For example, when I generate an url generateUrl(routeName, params)
, I want a router to generate a full url, including query params. All params that are not a part of a route itself should go to the query part.
For example:
/user/:username
generateUrl('user', {username: 'John', busy: 1})
should generate /user/John?busy=1
Of course, there should be a possibility to provide a function to generate query strings.
I know, you want to keep the router tiny and simple. So I'll not wonder if you decline this feature request. But:
generateUrl(routeName, params, queryParams)
, but I don't think that user of generateUrl
function should think what params are belong to route and what should go to query string.)Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
The following code in will never hit the /:cid subpath:
export default {
path: '/clients',
action: () => <Clients />,
children: [
{
path: '/:cid',
action(context) {
console.log('action /:cid');
return context.params.cid;
}
}
]
};
Hi,
When using Universal Router with React Starter Kit, any API requests using either fetch or Axios (for example in routes/home/index.js
) will call the API endpoint twice.
Any idea why?
Update March 25:
The double call only happens when server-side rendering is called. Accessing the route client-side triggers a single API request.
an example would be great
does it support anchor?
just like click on tag: <a href="#position1">jump</a>
,
the page scroll to the position of the tag: <a name="positon1">position1</a>
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
How can i set the url case-sensitive?
Now
/somEPAth/
/somePath/
/somepath/
got the same result.
Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.
Hello, guys!
Your router looks great on a slides and simple in docs. Actually, too simple.
It promos like 'isomorphic', but I can't find how to work with HTML5 History API and NodeJS http requests too. It seems this router can't capture nothing and I need to call function "resolve" manually each time the route is changed (on the server and on the client in different ways). So, could you explain which part of this router is "isomorphic" and why it's called "router", if it can't observe routing paths by itself?
Thanks!
--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/39745948-i-don-t-understand-how-universal-router-is-working?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F18115217&utm_medium=issues&utm_source=github).UMD builds must include path-to-regexp
and babel-runtime
libraries:
https://npmcdn.com/[email protected]/universal-router.js
https://npmcdn.com/[email protected]/universal-router.min.js
In order to fix that, edit UMD bundle configuration in /tools/build.js
.
Also, update README.md
file to include info how to use UMD builds from CDN.
Provide a mechanism to do smooth animated transitions to a different route.
Consider also the same route with different parameters. Ex: /user/123
to/user/124
.
Ideally it can work with ReactCSSTransitionGroup
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.