React-router API - defining routes - javascript

I am trying to use the react-redux-firebase app as a base for my app.
I'm trying to add static pages to the footer links (no state) and then render them as the app expects.
I haven't seen this approach to setting up a route index.js before.
The generator creates this index file (to which I have tried to add the About route):
import CoreLayout from '../layouts/CoreLayout'
import Home from './Home'
import LoginRoute from './Login'
import SignupRoute from './Signup'
import ProjectsRoute from './Projects'
import AccountRoute from './Account'
import NotFoundRoute from './NotFound'
import AboutPage from "./About"
/* Note: Instead of using JSX, we recommend using react-router
PlainRoute objects to build route definitions. */
export const createRoutes = store => ({
path: '/',
component: CoreLayout,
indexRoute: Home,
childRoutes: [
AccountRoute(store),
LoginRoute(store),
SignupRoute(store),
ProjectsRoute(store),
AboutPage,
// AsyncRoute(store) // async routes setup by passing store
// SyncRoute, // sync routes just need route object by itself
/* Place all Routes above here so NotFoundRoute can act as a 404 page */
NotFoundRoute(store)
]
})
/* Note: childRoutes can be chunked or otherwise loaded programmatically
using getChildRoutes with the following signature:
getChildRoutes (location, cb) {
require.ensure([], (require) => {
cb(null, [
// Remove imports!
require('./Counter').default(store)
])
})
}
However, this is not necessary for code-splitting! It simply provides
an API for async route definitions. Your code splitting should occur
inside the route `getComponent` function, since it is only invoked
when the route exists and matches.
*/
export default createRoutes
This app layout is that all the views seem to be setup under the routes folder. Following that approach, I made an AboutPage folder within src/routes/About/components. That folder has a AboutPage folder with a js page and in index, as well as separate index.js file (similar to the HomePage component provided with the generated app). The nested index has:
import AboutPage from './AboutPage'
export default AboutPage
The src/routes/About/index.js
import AboutPage from './components/AboutPage/index.js'
// Sync route definition
export default {
component: AboutPage
}
I don't get any errors when I save this, but when I start the server and click the link, I just get a 404 error.
How do I add routes to this app?
I have tried a million different variations on the import statement in the routes/index.js - many of which produce no error but do not actually render the page.
The full setup is src/routes/About/components/AboutPage/AboutPage.js
class About extends React.Component {
componentDidMount() {
window.scrollTo(0, 0);
document.body.scrollTop = 0;
}
render() {
const { classes, ...rest } = this.props;
const imageClasses = classNames(
classes.imgCard,
classes.imgFluid
);
const navImageClasses = classNames(classes.imgRounded, classes.imgGallery);
return (
<div>
-- intentionally deleted copy --
</div>
);
}
}
export default withStyles(profilePageStyle)(About);
Then, src/routes/components/AboutPage/index.js:
import AboutPage from './AboutPage'
export default AboutPage
Then src/routes/About/index.js
import AboutPage from './components/AboutPage/index.js'
// Sync route definition
export default {
component: AboutPage
}
Then routes/index.js - shown as above.
I have read this several times- I can't make sense of it. If the child doesn't have state, then I don't see why a store would be relevant.
https://github.com/ReactTraining/react-router/blob/v3/docs/API.md#plainroute

Related

Import JSON/JSONC file in vite/typescript project

want to share my routes between my actix-web backend and Vue w/ Vue-Router frontend without having to have to files describing the routes. So when I define the routes in the frontend, I don't want to change anything for the server. I don't know if there's a better solution, if so, please tell me :D
My routes.jsonc is in my src folder. Every route is of the following type:\
route: { path: String; name: String; componentName: String }
Then my routes-Strings are converted to the actual Components:
import { createRouter, createWebHistory } from "vue-router";
import ButtonArrayVue from "#/components/Home/ButtonArray.vue";
import App from "#/App.vue";
import * as routesImport from "../routes.jsonc";
const history = createWebHistory();
const routes = routesImport.map((route: { path: String; name: String; componentName: String }) => {
let component = function () {
switch (route.componentName) {
case "ButtonArray":
return ButtonArrayVue;
case "App":
return App;
}
};
return {
path: route.path,
name: route.name,
component: component,
};
});
export default createRouter({
history,
routes,
});
Now the problem is the 4th line:
I've tried multiple import statements, also:
import routesImport from "../routes.jsonc";
import routesImport from "src/routes.jsonc";
import routesImport from "#/routes.jsonc";
But I always get "Cannot find module '../routes.jsonc' or its corresponding type declarations" as a typescript and Compiler error. Sometimes also '"default" is not exported by "src/routes.jsonc", imported by "src/plugins/router.ts".'
Now to my question:
How can I import a json file without putting it in the public folder as some other questions suggest? I don't want to make this file public if possible.
Are there better alternatives? How could I import this json file?
You have to set resolveJsonModule to true in tsconfig.json; but note that this will ONLY work for files that are .json -- NOT .jsonc.

Why does export const work but export function does not?

I am trying to ensure that the same instance of vue-router is available in multiple places in my app.
The vue.router.js file exports the router like this:
export function createVueRouter() {
return createRouter({
history: createWebHistory('/')
routes: [//array of routes]
});
}
In my main app.js file (or main.js if you prefer) it is used like this:
import {createVueRouter} from './router/vue/vue.router.js';
const router = createVueRouter();
app.use(router);
The Vue app now has access to the vue-router. I now want the same vue-router to be available in an independent JS file. But the following does not work:
resusable.js
import {createVueRouter} from './router/vue/vue.router.js';
const router = createVueRouter();
//....
if (isLoggedInBasic !== true) {
return await router.push({name: "Signin"})
}
It creates a new instance of the vue-router and the router.push does not take the user to the Signin route (but the URL in the address bar does change).
If I change vue.router.js export to the following, it works fine all the files with the same router instance being shared between them.
export const VueRouter = createRouter({
history: createWebHistory('/')
routes: [//array of routes]
})
I cannot understand what is the difference and why it works when export const is used rather than just export function ()

React Router Authentication Async

I'm new to react.
I want to add some security to my Async-Routes which I implemented in my routes.js:
import React from 'react';
import App from './app.jsx';
import Home from './components/home.jsx';
import {Router, Route, IndexRoute, hashHistory} from 'react-router';
function loadRoute(cb) {
return (module) => cb(null, module.default);
}
const routes = {
component: App,
childRoutes: [
{
path: "/",
component: Home
},
{
path: "/hello/:foo",
getComponent(location, cb) {
System.import('./components/hello.jsx')
.then(loadRoute(cb))
.catch(errorLoading);
}
},
]
};
export default () => <Router history={hashHistory} routes={routes} />
As you can see the "/hello/:foo" route is async.
How can I restrict the access to this route (role-based) and redirect to somewhere else (e.g. login)?
I want to load the chunk only when it's needed.
Should I place the checking code into "getComponent()"?
Can it be done with "willTransitionTo()", will this function be executed before "getComponent()" and how should I implement it?
I would place the checking code into componentWillMount(), and in render() return the page component, or display/redirect to login.
If you have multiple page that need access restricted, I'd create a high level component order for each page component to check access before rendering.

How to navigate using vue router from Vuex actions

I am creating a web app with Vue 2.x and Vuex 2.x. I am fetching some information from a remote location via an http call, I want that if that call fails I should redirect to some other page.
GET_PETS: (state) => {
return $http.get('pets/').then((response)=>{
state.commit('SET_PETS', response.data)
})
},
error => {this.$router.push({path:"/"}) }
)
}
But this.$router.push({path:"/"}) gives me following error.
Uncaught (in promise) TypeError: Cannot read property 'push' of undefined
How can this be achieved.
Simulated JsFiddle: here
import router from './router'
and use router.push
Simple like that.
This example may help you.
main.js
import Vue from "vue";
import VueRouter from "vue-router";
...
Vue.use(VueRouter);
export const router = new VueRouter({
mode: 'hash',
base: "./",
routes: [
{ path: "/", component: welcome},
{ path: "/welcome", component: welcome},
]
})
actions.js
import {router} from "../main.js"
export const someAction = ({commit}) => {
router.push("/welcome");
}
It looks like you aren't injecting your router into your app, hence it being 'undefined'
In previous versions of vue-router you would: Vue.use(VueRouter), with 2.0 you can inject the router into the app like below:
const routes = [
{ path: '/foo', component: Foo },
]
const router = new VueRouter({
routes
})
const app = new Vue({
router // inject the router
}).$mount('#app')
this should then make it available as this.$router throughout the app
Following answering a related question: How to use Vue Router from Vuex state? it seems that Vuex won't receive the router instance at this.$router. Therefore two methods were suggested to provide access to the router instance.
The first is more direct which involves setting a webpack global to the instance.
The second involves using Promises with your vuex action that would allow your components to utilise their reference to the router instance following the actions Promise resolving / rejecting.
INITIAL ANSWER
In main.js (the one, where we "install" all modules and create Vue instance, i.e. src/main.js):
const vm = new Vue({
el: '#app',
router,
store,
apolloProvider,
components: { App },
template: '<App/>'
})
export { vm }
This is my example, but in our case the most important here is const vm and router
In your store:
import { vm } from '#/main'
yourMutation (state, someRouteName) {
vm.$router.push({name: someRouteName})
}
P.S. Using import { vm } from '#/main' we can access anything we need in Vuex, for example vm.$root which is needed by some components of bootstrap-vue.
P.P.S. It seems we can use vm just when everything is loaded. In other words we can not use vm inside someMutation in case, if we call someMutation inside mounted(), because mounted() comes/occurs before vm is created.
NEW ANSWER
Constantin's answer (the accepted one) is better than mine, so just want to show for novice how to implement it.
Inside core dir (inside /src in my case), next to App.vue, main.js and others I have router.js with the content:
import Vue from 'vue'
import Router from 'vue-router'
// Traditional loading
import Home from '#/components/pages/Home/TheHome'
// Lazy loading (lazy-loaded when the route is visited)
const Page404 = () => import(/* webpackChunkName: "Page404" */ '#/components/pages/404)
const Page503 = () => import(/* webpackChunkName: "Page503" */ '#/components/pages/503)
Vue.use(Router)
const router = new Router({
mode: 'hash',
base: process.env.BASE_URL,
linkExactActiveClass: 'active',
routes: [
{
path: '*',
name: 'Page404',
component: Page404
},
{
path: '*',
name: 'Page503',
component: Page503
},
{
path: '/',
name: 'Home',
component: Home
},
// Other routes
{....},
{....}
]
})
// Global place, if you need do anything before you enter to a new route.
router.beforeEach(async (to, from, next) => {
next()
})
export default router
Import our router to main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
Vue.config.productionTip = false
const vm = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
export { vm }
Finally, inside your component, or Vuex or anywhere else import router from './router' and do whatever you need, such as router.push(...)
I didn't like keeping my app's location state separate from the rest of my app state in the Store, and having to manage both a Router and a Store, so I created a Vuex module that manages the location state inside the Store.
Now I can navigate by dispatching actions, just like any other state change:
dispatch("router/push", {path: "/error"})
This has the added benefit of making things like animated page transitions easier to handle.
It's not hard to roll your own router module, but you can also try mine if you want to:
https://github.com/geekytime/vuex-router
You can simply import route from router directory like this:
import router from '#/router'
router.push({name: 'Home'})
this # symbol replaces the path to the src directory

Import index file for directory not functioning correctly

The following contents of the application.js file report that "home" being imported is undefined.
import {home} from "./routes/index"
console.log(JSON.stringify(home, null, 4))
The content of index.js are as follows:
export * from "./home.js"
The content of home.js are as follows:
export const type = "GET"
export const route = "/"
export const middleware = [];
export const action = function(req, res) {
res.send("Testing custom routes");
}
A picture of the directory structure is as follows:
You're not exporting anything named home. If you want to attach everything you are exporting to a variable named home then use as.
import * as home from './routes/index';
See here for more information on import/export.
You could structure your code like so to achieve the effect you want:
application.js
import { home } from './routes';
home.js
const type = "GET"
const route = "/"
const middleware = [];
const action = function(req, res) {
res.send("Testing custom routes");
}
export default {
type,
route,
middleware,
action
};
index.js
import * as home from './home.js';
export default {
home
};

Categories