I have an issue with how my React Redux SSR application is handling site navigation I have a route for list pages which will display different data depending on the params in the URL.
Routes.js file
export default [
{
...App,
routes: [
{
...HomePage,
path: '/',
exact: true
},
{
...ListPage,
path: '/list/:id',
exact: true
},
In my Index.JS file where my express backend is running I iterate through my routes directory to see the path(s) that matches the request path...
const app = express();
app.use(express.static('public'));
app.get('*', (req, res) => {
const store = createStore(req);
const promises = matchRoutes(Routes, req.path)
.map(({ route }) => {
console.log("Looking at Route: ", route);
if (route.loadData) {
const params = req.path.split('/');
console.log('my params are: ', params)
return route.loadData(store, params[2])
}else{
return null
}
})
.map(promise => {
if (promise) {
return new Promise((resolve, reject) => {
promise.then(resolve).catch(resolve);
});
}
});
Promise.all(promises).then(() => {
const context = {params: req.params};
const content = renderer(req, store, context);
if (context.url) {
return res.redirect(301, context.url);
}
if (context.notFound) {
res.status(404);
}
res.send(content);
});
});
My understanding is that there should only be 2 things to iterate over, the App component Route, and the ListPage component Route it then calls their respective loadData() functions and the websites continues to run. However after it goes through the first 2 routes and populates my page with the relevant information the Index.js file gets called again and iterates through the routes but this time instead of having the URL that the user is trying to access it replaces it with "bundle.js" and I don't understand what's going on here. This is the output I get I would love to only have the top half of the output.
NOTE this image is taken from my console (I've combined both the client and server side output in 1 window) below I'll include a screenshot of my config Files
Of course my code wasn't expecting this as a path and the application breaks because it's trying to get information on a list with the ID of "bundle.js" instead of a standard number.
Question can someone explain to me what my codes doing wrong here or if this is how it's supposed to behave how I work around this I'd greatly appreciate it.
I'm currently trying to create my first SSR application so I'm new to this technology so I might be missing something obvious.
Upon further investigation I noticed that the file bundle.js that I could see in the console was referring to a file at location /list/bundle.js but my bundle was actually in my public directory so I had to modify the script Src so that it would refer to the http://localhost:3000/bundle.js after I did this app functioned how It was supposed.
Related
As a first project I have Nuxt, VueJs and Capacitor that fails to route a Firebase dynamic link.
The iOS App is setup to accept Associated Domains as works as such.
The iOS App opens and fires a Capacitor event 'appUrlOpen', however it fails to navigate when I try initiate router.push() to any valid path eg.('/about')
Debugging suggests the promise on the push succeeds, however this is not reflected on the simulator.
I have ensured that the the routes are correct at the time of the push and exhausted all other possibilities to where this may be causing an issue.
The code below resides in a JS file which is imported as a plugin with nuxt.config.js
import { App } from '#capacitor/app';
import Vue from 'vue';
import VueRouter from 'vue-router';
const router = new VueRouter({ routes: [ { path: '/about' }] });
App.addListener('appUrlOpen', data => {
router.push({ path: '/about' })
.then(() => console.log('Navigated!'))
.catch(error => {
console.log('Error', error);
});
});
Oddly, a user by the name of Mani Mirjavadi (user:4448220) indirectly answered this question but removed his post a short while ago. Luckily it was available as a cached resource.
It appears as if the route needs to route as such below.
window.onNuxtReady(() => {
App.addListener('appUrlOpen', (event) => {
window.$nuxt.$router.push('/community')
})
})
I'm working on a project with next.js and Reactjs that uses a lot of different languages. So I need to change the language url. Example:
www.example.com/es/entradas
www.example.com/en/tickets
www.example.com/de/eintrittskarten
To make routes I saw that there is a module that helps me: next-routes
https://github.com/fridays/next-routes
There are a lot of url and I'm working with a CMS, so my clients will be able to add more, so routes can't be harcoded. I thought to pass the url with queries, like this:
const routes = require('next-routes');
module.exports = routes()
.add('index', '/:lang?')
.add('tickets', '/:lang?/:ticket')
.add('hotel', '/:lang?/:hotel');
My surprise (as you might see), it doesn't work because routes doesn't see the difference between these two last routes. If I write:
www.example.com/en/tickets
It will go correctly to my page "tickets" but if I write
www.example.com/en/hotel
It will go again to my page "tickets" and not to "hotel"
Do you know any way about how could I make this?
In my project I have these files related about routes:
server.js
const next = require('next');
const { createServer } = require('http');
const routes = require('./routes');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dir: './src/shared', dev });
const handle = routes.getRequestHandler(app);
app.prepare()
.then(() => {
createServer(handle)
.listen(3001, (err) => {
if (err) throw err;
console.log("> Ready on http://localhost:3001");
});
});
routes.js
const routes = require('next-routes');
module.exports = routes()
.add('index', '/:lang?')
.add('tickets', '/:lang?/:ticket')
.add('hotel', '/:lang?/:hotel');
The request for /en/hotel is going to the "ticket" route because it is actually matching it.
This is because of the : in front of the word "ticket" in the route. A : will turn a section of a route into a parameter with that name.
So instead what you probably want is:
module.exports = routes()
.add('index', '/:lang?')
.add('tickets', '/:lang?/ticket')
.add('hotel', '/:lang?/hotel');
The application I'm working on is based on React Fiber and React Router V3.
Trying to use hydrate() instead of render() with async components I've faced with the following issue: HTML returned from SSR is different from client-side one.
As a result React remounts the whole DOM and throws the following warning: Did not expect server HTML to contain....
React Training does not provide solution as well: Code-splitting + server rendering
Is there any solution to achieve this?
Updates:
Simple Example
(pseudo code)
App.js:
export default () => <div>Lorem Ipsum</div>;
client.js:
const createRoutes = store => ({
path: '/',
getComponent(nextState, cb) {
require('./App'); // some async require
},
onEnter: (nextState, replace, cb) => {
store.dispatch(fetchData())
.then(() => cb())
.catch(cb);
}
});
match({history, routes: createRoutes(store)},
(error, redirectLocation, renderProps) => {
hydrate(
<Router history={history} routes={createRoutes(store)} />,
document.getElementById('app')
);
});
server.js
match({routes: createRoutes(store), location: req.url},
(err, redirectLocation, renderProps) => {
const content = renderToString(<RouterContext {...renderProps}/>);
// send content to client
});
I've been investigated the issue a bit more deeper and found the solution.
To achieve DOM hydration the following point should be token in account:
I the example above in client.js I invoked createRoutes(store) twice. This is redundant because renderProps already has routes property prepared for <Route /> component. Due to this mistake onEnter was called twice, so data fetching was performed twice too.
To avoid HTML mismatch on server and client side data fetching in onEnter should not be called on the first client-side render.
match function waits for getComponent callback is performed before render. So the main question is wrong, because this functionality is available out of the box.
I'm working on a react web app using React, Redux, and React Router, with server side rendering (using express)
The problem I'm facing is a bit hard to explain. I will try to explain it in the following steps.
You first enter the app from a URL like http://www.example.com/articles/1234. The express server will send down the correct content which includes the correct page source and the DOM(from chrome Element panel) as the initial load.
Then let's navigate to a different page like http://www.example.com/articles/5678 (navigating is not causing page refresh since it's a single page app, it only reaches out to the server for SSR content when you do a refresh with your browser or entering a page from a URL in browser address bar) everything works fine so far.
Refresh the page in the browser (You're now on page http://www.example.com/articles/5678) with cmd + r or F5. Again, the server will send down the content. But this time the content that you're receiving is a bit different. The page content in the browser is correct as well as DOM from chrome Elements panel. However, the page source is not correct, it's still the old page source that you got from step 1.
I have tried to log out the content when refreshing the page. The content that the server sent down in step 3 is not the correct content (It's the previous page) but somehow the browser can still see the right content.
And if I refresh the page once more after step 3 then I will get both the correct content and correct page source...
I'm also using Facebook open graph debugger. The debugger is telling me that it follows a redirect to step 3. And the redirect URL it followed is the previous page URL. I know this not quite right, but not sure where I'm doing it wrong...
Here are my express server settings
Thanks for the help!
server.js
require('babel-register');
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const ReactRouter = require('react-router');
const ReactRedux = require('react-redux');
const Store = require('./src/store/configureStore').default;
const routes = require('./src/routes').default;
const compression = require('compression');
const morgan = require('morgan');
const store = Store();
const match = ReactRouter.match;
const RouterContext = ReactRouter.RouterContext;
const Provider = ReactRedux.Provider;
const port = process.env.PORT || 5050;
const ReactHelmet = require('react-helmet');
const Helmet = ReactHelmet.Helmet;
const app = express();
app.use('/dist', express.static('./dist'));
app.set('view engine', 'ejs');
app.use(compression());
app.use(morgan('combined'));
app.use((req, res) => {
// prettier-ignore
match(
{ routes, location: req.url },
(error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(302, redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
console.log(renderProps);
const body = ReactDOMServer.renderToString(
React.createElement(
Provider,
{ store },
React.createElement(RouterContext, renderProps)
)
);
const meta = Helmet.renderStatic().meta.toString();
res.status(200).render('index', {body, meta});
} else {
res.status(404).send('Not found');
}
}
);
});
console.log('listening on port ' + port); // eslint-disable-line
app.listen(port);
I have eventually resolved the issue.
The main problem was that I placed my async data fetching calls in the componentWillMount lifecycle method instead of componentDidMount.
And I didn't properly handle async data fetching in my App.
I'm now using the approach mentioned in this article to deal with my async data fetching issue.
Basically, I'm doing the following:
On the client side: Create a static method fetchData in components.
On the server side: Use React Router to grab the right component and use the fetchData method to fetch the data needed for that component.
After these promises have been resolved, get the current state from Redux store and render the HTML and send it down to the client.
Inject the state to the client side Redux store and render the page.
I'm building an app with file manager like functionality with Ember.js. I'd like the URL for nested folder in the form of ".../#/files/Nested/Inside/" and it works fine with linkTo; however if I refresh (or go to the URL directly) I have the error message "No route match the URL '/files/Nested/Inside'". Is there any way to make Ember.js works in situation like this? Thanks.
Here is my current route setup:
FM.Router.map(function() {
this.resource('folders', { path: '/files' })
this.resource('folder', { path: '/files/:path' })
})
FM.FoldersRoute = EM.Route.extend({
model: function() {
return FM.Folder.find('/')
}
})
FM.FolderRoute = EM.Route.extend({
model: function(params) {
return ns.Folder.find(params.path)
},
serialize: function(folder) {
return { path: folder.get('path') }
}
})
Wow, interesting question. It should be possible but I've not tried it myself or seen any examples of this in the wild.
Under the hood, ember uses the tildeio router and route-recognizer to resolve routes. The route's readme explains how to define more elaborate routes like:
router.map(function(match) {
// this will match anything, followed by a slash,
// followed by a dynamic segment (one or more non-
// slash characters)
match("/*page/:location").to("showPage");
});
So to get nested folders working, you might be able to do something like this:
FM.Router.map(function() {
this.resource('folders', { path: '/files' })
this.resource('folder', { path: '/files/*path' })
})
Hope this helps.