I am using Next JS.
Currently, my page is in the url
http://localhost:3000/project/613
Now, i want to push the page to
http://localhost:3000/project/613/time/123
Is there any way i can push relatively like router.push('/time/123')
Instead of entering the full URL router.push('project/613/time/123')
While it's not exactly the same as relative routing you can prepend router.asPath to the relative part.
router.push(`${router.asPath}/time/123`)
Meaning you don't need to explicitly set the beginning of the path.
We should define a public variable in .env and/or in .env.development (the prefix must be NEXT_PUBLIC)
NEXT_PUBLIC_BASE_PATH=/project/613
That variable is visible both client and server side. (It's probably ok, this is a part of the public url anyway)
Use that value in next.config.js
module.exports = {
basePath: process.env.NEXT_PUBLIC_BASE_PATH,
// ...rest of our config
}
If the link was a next/link component, we would be good to go, imported images works fine too, at some places need more work like in router.push:
router.push(`${process.env.NEXT_PUBLIC_BASE_PATH}/time/123`)
Alternatively you could just write
router.push(`${router.basePath}/time/123`)
However the .env approach is more general, you don't need useRouter everywhere like even in a simple image component, which displays some image from public static routes:
<img src={`${process.env.NEXT_PUBLIC_BASE_PATH}/logo.svg`}>
And for more options, if you don't want to do data fetching, you can add a shallow option. Check the docs: https://nextjs.org/docs/routing/shallow-routing
Yes. Just add / in the first:
router.push('/time/123')
Related
I have the need to be able to render any of the pages in my AEM model.json dynamically regardless of the current URL in a SPA React app.
My AEM model.json structure has pages following the /<country>/<language>/rest/of/path format, but I want to be able to strip the country/language and just use the rest of the URL path.
I am able to do this when I initialize the ManagerModel with a the desired path like this:
const path = `/path/to/<my_model>.model.json`
/* initialize the ModelManager with the path to the model.json */
ModelManager.initialize({ path })
/*
grab the desired section of the model and render by calling ReactDOM.render
By doing this I am able to render the section of the model that maps /us/en/user-account` for
example, and render the correct content even though the current browser path is `/`
*/
ModelManager.getData(`/us/en/<page_to_load>`).then(render)
When I handle navigation with history.push (I use react-router), I want to be able to render another page following the same logic. By default, having executed ModelManager.getData("/us/en/<page_to_load>"), every page that I navigate to then renders that same portion of the model.
To fix this, I have tried many variations of ModelManager.getData() calls with no success. The only thing that I have been able to have any success with is dynamically passing the path to the next page to render to a callback function that is defined on the index.js level and passed down as a prop to App.js. The callback triggers another ReactDOM.render call and loads the page correctly regardless of what the actual URL path is. That code looks something like this:
<App
reRender={(path) => {
/* manipulate the path so that it properly maps to the correct AEM model data */
const updatedPath = `/us/en/${path}`
/*
this works, but causes another ReactDOM.render call every time that the current page is
changed
*/
ModelManager.getData(updatedPath).then(render)
}}
/>
There are also cases where the page that has been navigated to doesn't have a corresponding path in the modelStore mapping. I am able to handle that like this:
const pathToInsert = `/<country>/<language>/${window.location.pathname}.model.json`
ModelManager.modelStore.insertData(pathToInsert)
ModelManager.getData(pathToInsert).then(render)
/*
I have been having issues with this, but can get the newly inserted model to load properly by
re-routing back to the current path from the current path
*/
this.props.history.push(window.location.pathname)
I have read and re-read the documentation here and I am unable to figure out the correct way to do what I want to do. The above solutions work for the most part, but are pretty hacky and I would like to find out the proper way to accomplish this. Any help is greatly appreciated!
Yes, I found a work around solution. The AEM library can't handle this requirement out of the box unfortunately. My solution was to make a wrapper for my App component. In that wrapper I initialize the model manager, clone the data and store it in local stage. Then you can conditionally modify the model and pass it along as a prop to your App component. If you have a good understanding of how AEM model data is mapped you should be able to figure out a way to display what you want. You can also fetch and insert models into your master model's ":children" prop (think that is the field name, have not looked in a while).
I carefully read the docs of next routing system.
It only mentions that I could achieve dynamic routing like this:
http://localhost:3000/level1/dynamicSlug
But I am trying to achive something like this:
http://localhost:3000/level1/level2/dynamicSlug
And I want level2 to be created dynamic too
Thanks so much !
It is possible to do nested scenarios according to your request in this way.
for example:
pages/
level1/
[dynamicSlug]/
- index.js // will match for /level1/1234
level2/
- index.js // will match for /level1/level2
- [dynamicSlug].js // will match for /level1/level2/1234
Or
pages/
level1/
[dynamicSlug]/
- index.js // will match for /level1/1234
level2/
- index.js // will match for /level1/level2
[dynamicSlug]/
- index.js // will match for /level1/level2/1234
You have 2 choices:
Using v9 Dynamic Routing by calling the folder as [dynSlag] and putting your page file inside.
Using custom server and routing, you will need to define a custom server, map your path to a specific next page.
I know this is a bit of an old post, but I'd just like to share my working response with NextJS v11.
I want dynamic routing at two levels. E.g.:
{siteroot}/dynamicPage
{siteroot}/dynamicUrlSection/dynamicPage
My folder structure is:
/pages/[section]/[page].tsx
/pages/[section]/index.tsx
This way, the "dynamicPage" path at the root is handled by index.tsx, and nested routes are handled by [page].tsx
BONUS INFO: I am working with Contentful as a CMS. I use a single content model for all the pages at both levels.
The model has "section" and "page" properties.
The entries that serve the root dynamic pages (i.e. /pages/[section]/index) have a compound value in the "page" property of {section}-index. I then have to be a bit smart in my client code:
if (!page) {
page = `${section}-index`;
}
await fetchData(section, page);
Using the example right from the NextJs documentation, I use this hack, maybe you could use it.
<Link href="/posts/[id]" as={`/posts/${subFolder}${id}`}>
"as" will have a value like /posts/nested_subfolder_file.md
And in the getPostData function, just do this little change:
const nestedPaths = id.split('_')
const fileName = `${nestedPaths.pop()}.md`
const fullPath = path.join(postsDirectory, ...nestedPaths, fileName)
I have a requirement to select, from a local source, an image based on the value passed back from the REST API I am using. For example:
//Psuedo-call from the API
var imageIdToSelect = response.data.imageId
//Then later in the render()
<img src={ baseUrl + imageIdToSelect } />
I have a solution to this, which is to use require() as that allows me to append the url as such:
<img src={ require(baseUrl + imageIdToSelect) } />
This works fine, however, I am using a Microsoft TSLINT setup that does not allow require() over the prefered import at the top of the file "no-require-imports".
I know I am not meant to let linting tools control my work to the point where I am just blindly following rules. So my question is two-fold:
Why is it frowned upon to use require() in such a way. One reason I could think of is that if all of the external files/resources are declared at the top of the file, then you don't have to look through the source to find them hidden in functions.
What would the import x from './' solution look like here? I have seen people creating index.js and index.d.ts files inside their image folders to import and export all the images inside but that seems a tad extraneous.
Edit: I also have just realised that using require() with a non-literal string is a violation of my ts-linting too.
Thanks in advance
Is there a way to pre-generate the HTML structure of a (single route) React application directly in the HTML entry point?
Then the page will be able to display HTML (based on React initial state) before any JS is loaded.
I'm actually using webpack-html-loader but any other loader or plugin is welcome ;)
PS: May static-site-generator-webpack-plugin be of any help?
PS: I'm not using React Router
If you want to use static-site-generator-webpack-plugin you first need to build a bundle with webpack bundle.js that exports a render function that takes following arguments.
locals an object with various page metadata e.g. title that go into component parameters (traditionally thought of as template variables).
callback a nodejs style (err, result) callback that you will call with your rendered html as the value for result
e.g.
// entry.js, compiled to bundle.js by webpack
module.exports = function render(locals, callback) {
callback(null,
'<html>' + locals.greet + ' from ' + locals.path + '</html>');
};
It is in this function that you will instantiate your components (possibly via React Router if you want) and render them with ReactDOMServer.renderToString().
You will then specify the compiled bundle.js as bundle in your instantiation of StaticSiteGeneratorPlugin as well as your concrete routes in paths and in locals an object containing the above mentioned metadata values.
var paths, locals; // compute paths from metadata files or frontmatter
module.exports = {
entry: {
bundle: './entry.js' // build bundle.js from entry.js source
},
...,
plugins: [
new StaticSiteGeneratorPlugin('bundle', paths, locals)
]
}
The keys you specify for locals in webpack.config.js in will be present in the locals parameter of every call to render(locals, callback). They will be merged with path, assets and webpackStats keys provided by the plugin.
If you want to load javascript code into your pages after rendering you could compile an additional page.js entry to your webpack config that calls ReactDOM.render() in the typical manner and then load that bundle in a script tag emitted by in your render(locals, callback) function in your bundle.js (above). Ensure that page.js mounts components to the same location in the DOM as they are when rendered by entry.js (you will probably set an id attribute on the parent element). You will also need to ensure that any location (i.e. route path) dependent variables align in both environments.
Check out the source code of Gatsby which also uses this plugin. You can also have a look at the source code for Phenomic for an alternative approach.
You should try server side rendering, it will let react render the first view of your app in a backend and deliver a static HTML. This boilerplate already comes with server rendering set up and you can learn more about it here
Jekyll is great static site generator which can be extended with custom ruby plugins. You need to enable WebPack to make Jekyll calls. See Plugging Webpack to Jekyll Powered Pages
Here you have a working example https://github.com/aganglada/preact-minimal/blob/master/config/webpack.config.js, if you like you can fork it and take a look at how this work all together.
Hope it helps :)
Hopefully this is a very simple question:
I want to create a string containing the full URL to a page on my site, like:
https://example.com/documents/1
Ideally I'd like to generate this in a react-redux connect()-ed container, in mapStateToProps(). Where the page is a grandchild of a react-router Route using browserHistory (so maybe I can get it from react-router or browserHistory somehow?) If necessary I'd do it in a full-on React class.
But I can't for the life of me find out how to do this. window (as in window.location) is always undefined in mapStateToProps() (no big surprise there) and in my view components' render() function.
So far I have only found ways to access the current route, e.g. "/documents/1", which is only a relative path and does not contain the site base url (https://example.com/)
So firstly, remember your code runs on client and server so any access to window needs to be wrapped in a check.
if (typeof window !== 'undefined') {
var path = location.protocol + '//' + location.host + '/someting'; // (or whatever)
} else {
// work out what you want to do server-side...
}
If the URL you generate shows in the DOM, and on the client you populate the store with this URL before the first render, you're going to get a checksum error. If you really want to avoid the error you can wait until componentDidMount() in your root component to get/set the url.
After componentDidMount you can have direct access to the origin location so that you can get the root url.
I use the below code in my JSX all the time so that i dont have to worry about the root path ex: dev env this would be localhost:3000/whatever, staging this will be AppStaging.heroku/whatever/..... and in production it would evaluate to www.myapp.com/whatever
`${window.location.origin.toString()}/whateverRoute/`
You Can try!!
window.location.origin
you can try the URL API. Take a look at this link for more details https://developer.mozilla.org/en-US/docs/Web/API/URL
const url = new URL("blob:https://mozilla.org:443/")
console.log(url.origin);
// Logs 'https://mozilla.org'