Using withRouter as a wrapper with custom server, shallow routing doesn't seem to be working.
I currently use this method to change the route:
this.props.router.push({
pathname: currentPath,
query: currentQuery,
});
router prop comes from using withRouter to wrap my class component.
And couldn't figure where to put the shallow flag. So I switched to the method mentioned in the docs:
this.props.router.push('/post/[pid]?hello=123', '/post/abc?hello=123', { shallow: true })
So I did that manually, but I started getting 404s.
http://localhost:3000/_next/static/development/pages/search/%5Btype%5D/%5Bcat%5D/%5Barea%5D.js net::ERR_ABORTED 404 (Not Found)
decoded:
"http://localhost:3000/_next/static/development/pages/search/[type]/[cat]/[area].js"
I tried using :type instead of [type] but it also didn't work.
This is how it's configured on the server:
if ('/search/:type/:cat/:area' === route.path) {
return app.render(req, res, route.page);
}
Folder Structure:
/pages/search/index.js
I think this structure has something to do with the problem, since it's in the index.js and not just a plain file in the pages folder.
It should not reload the page while changing the route, that's the main thing I'm trying to accomplish.
I'm implementing SSR pagination, and I'm planning to use shallow routing to make page changes happen on the client instead of the server. Meaning achieve SSR on first load only, keep user in without refreshing.
I also tried this:
server.get('/search/:type/:cat/:area', (req, res) => {
console.log("reached here..."); // this gets logged
return app.render(
req,
res,
'/search/[type]/[cat]/[area]',
req.params
);
});
But I'm getting 404s, the page is not there now!
This also didn't work:
this.props.router.push(
`/search/[type]/[cat]/[area]?${stringifyQs(currentQuery)}`,
{
pathname: currentPath,
query: currentQuery,
},
{ shallow: true }
);
This should work:
server.js
server.get('/search/:type/:cat/:area', (req, res) => {
return app.render(req, res, '/search', {
...req.params,
...req.query,
});
});
pages/search/index.js
props.router.push(
'/search?type=foo&cat=bar&area=baz&counter=10',
'/search/foo/bar/baz?counter=10',
{ shallow: true }
);
Linked issue from GitHub
Related
I have a redux action which fires whenever a user on a site tries to change their profile. It does this through an api call middleware that I wrote using a tutorial from Mosh.
export const updateUserProfile =
({ id, name, email, favouriteThing, password, confirmPassword }, headers) =>
(dispatch) => {
dispatch(
apiCallBegan({
url: `api/users/profile`,
data: { id, name, email, favouriteThing, password, confirmPassword },
headers,
method: 'put',
onStart: userUpdateRequested.type,
onSuccess: userUpdateReceived.type,
onError: userUpdateFailed.type,
})
);
};
The app routes are like this:
<Route path='/profile' render={ProfileScreen} exact />
<Route path='/profile/page/:pageNumber' render={ProfileScreen} exact/>
I have pages on the profile page route because I have a React component which displays all the items that the user has created in a table, and I need the table to handle multiple pages. I couldn't figure out how to make a single component within a page change pages without changing the whole page, so I made it that the whole page changes.
The endpoint is this:
router
.route('/profile')
.put(protect, updateUserProfile);
I submit the new data using a submit handler which looks like this:
const submitHandler = (e) => {
e.preventDefault();
if (password !== confirmPassword) {
setMessage('Passwords do not match');
} else {
dispatch(updateUserProfile(data, headers));
}
};
This all works fine when I am at this URL: http://localhost:3000/profile
When I go to page 2 of the profile this is the URL: http://localhost:3000/profile/page/2 and I get the following error:
404 Not Found trying to reach /profile/page/api/users/profile
I logged location.pathname when the submitHandler is triggered and I get '/profile' which appears correct.
However when I log the req.originalURL at the error middleware in express I get /profile/page/api/users/profile
So something is changing the originating URL between when the function is called in the frontend and how it is received in the backend.
Is there anyway to keep req.originalURL at '/profile' irrespective of what page of the profile I am on? I have tried using the Switch component in React Router Dom and setting the location of profile pages route to '/profile' but this breaks the profile pages route and the profile page won't load at all when you click on next page.
I've done a lot of googling and testing in the app and I can't seem to think of anything, which makes me think I have a fundamental misunderstanding of what is going on here. I'd really appreciate some help if someone has any thoughts.
Turns out it was a very simple fix that someone pointed out to me on another site. Thought I would share it here.
There needs to be a forward slash at the beginning of the url in the request, otherwise it is interpreted as a relative path.
url: `/api/users/profile`
I sent Axios Request in the React Component as always and succeeded.
server > routes > product.js
router.post("/removeImages", auth, (req, res) => {
req.body.images.map(imgPath => {
fs.unlink(imgPath, (err)=>{
if(err){
console.log(err);
}
console.log('Image removed successfully');
});
});
});
client > src > components > FileUpload.js
import Axios from 'axios';
function FileUpload(props) {
...
Axios.post('api/product/removeImages', { images: Images });
So, I copied the line(Axios.post('api/product/removeImages', { images: Images });) and used it in the child component. Then, the following error occurred:
POST http://localhost:3000/product/api/product/removeImages 404 (Not Found)
I thought it was an Axios bug, so I changed it to a fetch(), but I got the same result (404 Not Found). I do not know how hard-coded URL can change depending on the component. Maybe it was a React bug?
It looks like you're missing a starting / in the url. When not starting the url with /, it will be relative to whatever your path is currently.
Meaning if you're currently in localhost:3000/products, any requests to the relative path api/products/removeImages will resolve to localhost:3000/products/api/products/removeImages
A call to /api/products/removeImages, with the staring /, will only be relational to the base url, in this case localhost:3000, not the current path, thus result in locahost:3000/api/products/removeImages.
tl;dr: Add a prefixing / to your urls to resolve your relative path issue.
Additionally, as #ataravati mentioned in the comments, best practice is to also set a baseURL for axios.
I am having this problem that whenever i try to visit the page localhost:3000/blog/test directly it returns a 404 error. But whenever i try to visit it using <Link> component it works fine.
This is my code <Link href={{ pathname: '/blog', query: {slug: 'test'} }} as="/blog/test"><a className="nav__link">Blog</a></Link>
and i have a file blog.js in my pages folder.
What's happening is that on the client, with the Link component, you are creating a link to the blog.js page by setting "/blog" as the pathname.
When you go directly to the URL/blog/test, Next.js will try to render the page on the server and to do so will look for the file /pages/blog/test.js. That file doesn't exist, and Next.js doesn't know that you want to load the blog.js page and set query.slug to to the second part of the URL.
To do this, you need to map that route on the server to load the page you want, and pull the params you want out of the URL.
The Next.js docs cover this in Server Side Support for Clean URLs by using express to setup a custom server.
You can view the full code to get it working there, but your custom route will look something like this:
server.get('/blog/:slug', (req, res) => {
const actualPage = '/blog'
const queryParams = { slug: req.params.slug }
app.render(req, res, actualPage, queryParams)
})
You'll have to use now.json to set up your routes. Also it is important to note that it's now that builds the route so visiting it on the client side wont work if you are using localhost. Build your project with now and it should work.
Also the "as" parameter would be as={{ pathname:/user/manage/${variable}}}
I have several components displayed with react router that have dynamic url paths. An example path I have is
<Route path="/newproject/:id" onEnter={checkSesh} component= {ProjectDetails} />
When entering this component, I have a componentWillMount function that extract the id part of the url so that I can get the info for the correct project and render it on the ProjectDetails component.
componentWillMount() {
var id = this.props.router.params.id
this.props.teamDetails(id);
}
this.props.teamDetails(id) this calls a redux action creator that will make an axios request to an express route that will get the project info from the database.
export function teamDetails(id) {
return function(dispatch) {
axios.get('/getteaminfo/' + id)
.then(res => {
dispatch({ type: "SET_TEAM_DETAILS", payload: {
teamInfo: res.data.teamInfo,
admin: res.data.admin,
adminID: res.data.teamInfo.teamAdmin,
teamMembers: res.data.teamInfo.teamMembers
}
})
});
}
}
everything works fine upon visiting the page after already being logged in etc. But when I refresh the page /newproject/:id, i get an error Uncaught SyntaxError: Unexpected token <. An example url in my browser looks like http://localhost:3000/newproject/58df1ae6aabc4916206fdaae. When I refresh this page, I get that error. The error is complaining about my <!DOCTYPE html> tag at the very top of my index.html for some reason. This index.html is where all of React is being rendered.
When page is refreshed store state is not preserved. Make sure the state is not important to load the page, or at least initialized properly every time.
For e.g. login information if saved in store and not on browser with localStorage or cookies etc.. then on refresh, the error will come when trying to access /getteaminfo/ route through axios. The response will have error html and it can't be parsed by js.
Please check your web console on for more information. You can use chrome extension like https://github.com/zalmoxisus/redux-devtools-extension which will show your store and etc..
Make sure to check what /getteaminfo/ gives with id is not passed.
Also, make sure on your server side, did you route requests to react-router path through something like this?
e.g. express js,
app.get('*', function response(req, res) {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
be sure to sendFile with the real location of index.html
I found the answer here: react-router dynamic segments crash when accessed I added <base href="/" /> into the <head>of my index.html. You can also read more info here: Unexpected token < error in react router component
I'm using react-router for server side rendering and I have locale information stored in a locales.json file. The locale information is only set after a response from an api call, which includes the current language, i.e. 'GB', 'NO', 'FR', etc., which then completes the server response and everything is fired through to the client in the correct language.
However, I'm using the react-router match method:
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => { ... }
...and I need the routes to be based off the language from the api response, i.e.
// Route
<Route path={`:storeId/${locales[language].path}`} />
// locale.json
{
"GB": {
"path": "contact"
},
"NO": {
"path": "kontakt"
}
}
Is this approach possible? It's like I need to define routes after the api call is made, but to make the api call, I need routes defined.
Yes, I haven't tried your example specifically, but it is definitly possible to define routes from an api response and pass it to the "match" function.
You could try something as following:
function handleServerRendering(req, res) {
axios.get('http://localhost:3001/myCutomeRoutes')
.then(function(response){
const myRoutes = {
routes: response.data,
location: req.url
}
match(myRoutes, function(error, redirectLocation, routeContext) {
// WRITE YOUR CODE IN HERE...
})
})
.catch(function(err){
console.log('error', err);
})
}
As you can see, you firstly do an API call and then you pass the response.data to routes inside "myRoutes" constant