Build optionally layouts in NextJS by detecting the browser URL value - reactjs

all I would try to make some optional layout in NextJS. My problem is that I have found some method to check the browser URL then serve content according to these URL. But by this way the server content an browser content are no longer identical, hence the schema breaks.
Here an snippet of my trial:
export default ({children, title = 'title' }) => {
var
contact = false;
if (exenv.canUseDOM ){ if (window.location.href==="http://localhost:4000/contact" ) { contact= true}; }
else {const contact = false};
if (contact){ return( <div>Hey it is contact ! </div> ) }
else { // return normal layout
My console returns me predictably:
Warning: Text content did not match. Server:
So... I'm wondering if I have to make some custom settings on a custom NextJS server to grant the backend/frontend behaviors?
Or maybe there is a less expensive solution in the area?
Any hint would be great,
thanks

You can't remove that warning, it is appear because you just render that layout from client side, so there is a different between layout rendered by server and layout rendered by client.
I don't know what variable exenv.canUseDOM is, but you can use process.browser to detect if the current process is server side rendering or not.

Related

Next.JS: Make server side fetch depend on client side select input

First up, I'm new to Next and I'm using Next 13.1 with the app directory.
I need a select input in my application that is used to switch between the different years of data I'm looking at.
The data of the entire application (content, side navigation bar, top navigation bar) depends on the selected year, because I need to fetch data from different paths depending on which year is selected.
Currently I am fetching all the data server side, and I am setting a searchParam when the select input value changes. Because the search params can only be accessed in Page components on the server side, I'm converting the search param to a request header using middleware, so every server-side component can access it. This leads to a complete reload of the page every time a link is clicked.
My question is: How do I implement the functionality described above? I am obviously doing something wrong, but I'm new to client / server components and therefore don't know the best design practices.
// The select input:
<StyledTextField select defaultValue={currentYear ?? years.find(year => year.is_active)?.id}>
{years.map(year=> (
<MenuItem value={year.id} key={year.id} className={"yearSelectItem"}>
<Link href={{pathname: pathname, query: {year: year.id}}}>
{year.name} {year.is_active && "(aktiv)"}
</Link>
</MenuItem>
))}
</StyledTextField>
// The middleware:
const requestHeaders = new Headers(request.headers);
requestHeaders.set("x-year", request.nextUrl.searchParams.get("year") ?? "")
const response = NextResponse.next({
request: {
headers: requestHeaders
}
})
return response;
// This is how I access the header in server-side components:
headers().get("x-year")

How to Navigate to separate URL in react router Dom v6?

i am making a validation form , with a function that checks for password and email , if true it should navigate to a separate website (not to a path within the web). But it is not working
This is my code below:
function validate() {
if (email==='123#123.com' && password==="123456"){
console.log(email);
history('http://www.dummy.com/webmail')
}
}
return{
.
.
.
<Button className={stylesAuth.submitButton} variant="warning" onClick={validate}>
}
How should I do so?
Since you're redirecting to an external website, you can simply use plain old JavaScript and do a:
window.location.href = "https://www.dummy.com/webmail";
For more information, see https://developer.mozilla.org/en-US/docs/Web/API/Location/href.
A little bit more information: using history doesn't make sense in this case, it is only meant for internal navigation within your web application. In fact, you cannot even push an external URL onto the history stack:
The new URL must be of the same origin as the current URL; otherwise, pushState() will throw an exception.
https://developer.mozilla.org/en-US/docs/Web/API/History/pushState

Pdf Tron error " Exception error: Pdf error not found" (React App)

I'm trying to embed Pdf tron to my React application. I'm receiving this error when I'm clicking on the tab I want to filter to find the relative pdf file.
const handleFilteredDocs = (id)=>{
const filteredDoc = props.location.documents && props.location.documents.filter(doc=>{
return doc.controlId === id
})
setFileteredDoc(filteredDoc)
setPdfPath(filteredDoc[0].filePath)
WebViewer(
{
path: 'lib',
initialDoc: `lib/pdf/${pdfPath}`,
extension: "pdf"
},
viewer.current,
).then((instance) => {
const { docViewer, Annotations } = instance;
const annotManager = docViewer.getAnnotationManager();
docViewer.on('documentLoaded', () => {
const rectangleAnnot = new Annotations.RectangleAnnotation();
rectangleAnnot.PageNumber = 1;
// values are in page coordinates with (0, 0) in the top left
rectangleAnnot.X = 100;
rectangleAnnot.Y = 150;
rectangleAnnot.Width = 200;
rectangleAnnot.Height = 50;
rectangleAnnot.Author = annotManager.getCurrentUser();
annotManager.addAnnotation(rectangleAnnot);
// need to draw the annotation otherwise it won't show up until the page is refreshed
annotManager.redrawAnnotation(rectangleAnnot);
});
});
}
I'm thinking is because the ref component didn't receive in time the pdfPath state and then throw the error. I've tried to place a separate button to load the pdf with the pdfPath correctly updated and in that case worked. What can i do make it render correctly there?
this is the error I get from the console:
(index)
Value
UI version "7.3.0"
Core version "7.3.0"
Build "Mi8yMi8yMDIxfDZmZmNhOTdmMQ=="
WebViewer Server false
Full API false
Object
CoreControls.js:189 Could not use incremental download for url /lib/pdf/. Reason: The file is not linearized.
CoreControls.js:189
{message: "The file is not linearized."}
CoreControls.js:189 There may be some degradation of performance. Your server has not been configured to serve .gz. and .br. files with the expected Content-Encoding. See http://www.pdftron.com/kb_content_encoding for instructions on how to resolve this.
CoreControls.js:189 There may be some degradation of performance. Your server has not been configured to serve .gz. and .br. files with the expected Content-Encoding. See http://www.pdftron.com/kb_content_encoding for instructions on how to resolve this.
CoreControls.js:189 There may be some degradation of performance. Your server has not been configured to serve .gz. and .br. files with the expected Content-Encoding. See http://www.pdftron.com/kb_content_encoding for instructions on how to resolve this.
81150ece-4c18-41b0-b551-b92f332bd17f:1
81150ece-4c18-41b0-b551-b92f332bd17f:1 PDFNet is running in demo mode.
81150ece-4c18-41b0-b551-b92f332bd17f:1 Permission: read
CoreControls.js:922 Uncaught (in promise)
{message: "Exception: ↵ Message: PDF header not found. The f… Function : SkipHeader↵ Linenumber : 1139↵", type: "InvalidPDF"}
Thank you guys for any help I will get on this!
The value of "pdfPath" isn't set to "filteredDoc[0].filePath" yet after you call "setPdfPath" (it'll still be the initial state till the next render). One thing you can do is pass a callback function when using "setState" to call "WebViewer()" after "pdfPath" has been updated
https://reactjs.org/docs/react-component.html#setstate
Also there is a guide on how to add PDFtron to a React project in the following link
https://www.pdftron.com/documentation/web/get-started/react/
One thing to note, is it's does the following
useEffect(() => {
// will only run once
WebViewer()
}, [])
By doing the above, "WebViewer" is only initialized once. It might be a good idea to do something similar and use "loadDocument" (https://www.pdftron.com/documentation/web/guides/best-practices/#loading-documents-with-loaddocument) when switching between documents instead of reinitializing WebViewer each time the state changes

Given a moz-extension:// URL opened by bookmark, how can I switch to tab using extension code?

This is related to some other questions I am working on.
Say, for whatever reason, a user has bookmarked a page (call it pageURL of the pattern moz-extensions://MY-OWN-WEBEXT-ID/*) intended to be opened from a browser action context menu, and opened it in a tab, then opened many other tabs and possibly other windows. User knows the extension tab exists somewhere and doesn't want to keep opening new bookmarks, and so wants to use the add-on's browser action context menu to find the extension page's tab. Likewise, I don't want my add-on to open a duplicate tab.
The problem, since the add-on did not create the tab (a bookmark did), I have no tab ID to pass to browser.tabs.update( WebExtTab.id, { active: true } ) or window ID to pass to browser.windows.update( WebExtWindow.id, { focused: true } ). (WebExtWindow referring to a WebExtensions browser.windows.Window object, not a browser window object.
I can use browser.extension.getViews( ) to generate a list of browser window objects (aka tabs), and checking each window.location.href find that indeed the URL (and thus tab) does exist (somewhere), but I can't use that window object to focus on the tab nor to get a tab ID for browser.tabs.update().
In the case of multiple browser windows, I can't even get the right browser window to raise up given that window object, because the window objects returned by getViews have no id property with which to call browser.windows.update(). Similar to the tabs problem.
Finally, I can't use browser.tabs.query( { 'url': pageURL } ) to find the tab ID, because the url option must conform to match patterns, which FORBID using the moz-extension:// scheme.
What would be exceptionally useful was if the WebExtensions API allowed an extension to find the tabs and windows of all pages that belong to itself, regardless if those pages were opened by the add-on, manually entered, a bookmark or clicking a link.
For example, given a pageURL conforming to moz-extension://MY-OWN-WEBEXT-ID/*, one could do a browser.tabs.query and/or a browser.windows.query on a url matching the above pattern, and return a WebExt tab/window object, respectively. If such a tab/window was not opened by the WebExt API (i.e. bookmark), then generate a new object (i.e. a pseudo-create), to populate with existing data (i.e. location.href, status flags, etc) and generate new data as needed (i.e. the ID numbers), such that the returned object is usable within the context of the API.
This would fill a gap in API coverage where certain methods (i.e. getViews) return dead-end browser objects which have no hooks and no connection with the WebExt API and are thus mostly useless.
The simple answer: ++RTFM. browser.windows.getAll() will allow you to populate the windows objects with tab info. You need the permissions: [ "tabs" ] in manifest.json to get the tab.url property. But other than that, all the windows and tab objects will have an ID so that you can trivially focus window and switch active tab!
Note: This requires Firefox 52.0+ to make use of the async/await feature. Otherwise, you just have to use function generators and promises. Also, I've omitted any error checking, for demonstration purposes, but it might be a good idea to put them back in later.
async function tabCreate ( opts ) {
var pageURL = browser.runtime.getURL( opts.page + '.html' );
var extWins = await browser.windows.getAll( { populate: true, windowTypes: [ 'normal' ] } );
// Look for tab by comparing url, if url matches (i.e. tab exists), then focus window and make tab active.
for ( var extWin of extWins ) {
for ( var extTab of extWin.tabs ) {
if ( pageURL === extTab.url ) {
console.log( `My Extension->tabCreate(): Window ${extWin.id}, Tab ${extTab.id}:\n\t${extTab.url}` );
browser.windows.update( extWin.id, { focused: true } );
browser.tabs.update( extTab.id, { active: true } );
return;
}
}
}
// Otherwise, create tab.
browser.tabs.create( { url: pageURL } );
}
Opinion: I wish I didn't have to give away the tabs permission just for this feature. It would be nice if we always got our own moz-extension://MY-OWN-WEBEXT-ID/* urls, and null URLs for other tabs, without permissions given to access all tabs, but oh well.
Example Usage:
function myWebExt_Options ( ) {
tabCreate( {
'page': 'options',
'panel': 1
} );
}
browser.contextMenus.create( {
title: 'Options',
contexts: [ 'browser_action' ],
onclick: myWebExt_Options
} );
Note: I've implemented this to expect options in an opts object that has a page property, which I use as a shorthand to generate the full page URL. This is because of another question which requires passing a message to the page, which I store in opts.panel. But none of that is necessary. It could be changed to a flat string, or use the full 'getURL' generated elsewhere as a parameter. Change to suit your need and style.

Router in backbone not handling route after initialization

I have multiple routers in my app, in general way it looks like this:
// Start backbone.js
if (!Backbone.History.started) {
Backbone.history.start({pushState: true, hashChange: false});
}
// Perform some RPC requests ...
// Depending on user role, received from the server should be created suitable router:
var router;
if (typeof app.user.role === 'manager') {
router = new routers.manager();
} else {
router = new routers.guest();
}
Problem is that after page is loaded and script is executed routers do not do. anything. They do not load route for current url automatically. So, i had to fix it this way (i am not sure that it is a right way):
routers.guest.initialize = routers.manager.initialize = function() {
var defaultRoute = 'default';
if (typeof this.routes[Backbone.history.fragment] !== 'undefined') {
this[this.routes[Backbone.history.fragment]]();
} else {
this.navigate(defaultRoute, true);
}
};
It is working fine, except one bug: when i use route with params, for example /reset-password-confirm/:code - it is unable to find in in routes property. I could write some more code to fix it, but i suppose that i am doing something wrong, if i have to write such things - as i understand router should handle routes just after it was created.
So, questions:
Why my router(s) does not handle routes for current url after it is being created? Perhaps i need to start backbone history later? (but this bug will happen again later then)
How it is possible to make routes with params like /user/:id work there?
Perhaps it is bad idea to re-create routers? Perhaps it is better to create all of them one time?
P.S. I've tried to create both routers and keep them, also i've trie to call backbone history start method after all routers were created.. but this didn't help :/
Assuming you route is declared as the following:
routes : {
'/user/:id' : 'user'
}
Your initialize code is not working because when you initialize your router with a url such as: /user/1234. Backbone.history.fragment will be /user/1234 (not /user/:id). Since the this.routes object doesn't have a key of /user/1234, your else clause calls the default route.
If you first instantiate your router then call Backbone.history.start(), you will be able to remove your router initialize code. When you navigate to a url as /user/1234 your router will match the /user/:id route and call the user function.
The following should work for you without adding your initialize code:
var router = (app.user.role === 'manager') ? new routers.manager()
: new routers.guest();
Backbone.history.start({pushState: true, hashChange: false});
Looking at the code, seems like you're starting the backbone history before initializing any routes. That's most likely not goning to work.
The correct way of doing this type of seperation is by creating all the routes based on the role received from the server and then start the backbone history. Here's an SO thread that talks about it with code samples as well : How to protect routes for different user groups

Resources