React/nextJS: How to debug different nodes of SSR react application? - javascript

I'm running a nextJS application, which is running SSR.
But as I do get the error:
Warning: Did not expect server HTML to contain a <div> in <div>.
So there seems to be a difference between the server-side and the client side nodes. How can I find those differences?
This is the repo of an example app:
https://github.com/jaqua/nextjs-app
Just run npm install and npm run dev

As comparing two html manually can be rather cumbersome depending on the size of your page, it's advised to first assess what could be wrong rather than brute-forcing. From my experience in 99% of the cases an SSR mismatch occurs when you either:
Included and rendered a Component which doesn't behave the same way on the client and the server (e.g they use global variables to determine where the code is being run and conditionally render elements based on that). For example there was a clipboard module that would only work on the client because it would use a variable of window.
Rendering of data fetched from an asynchronous source which is only present on either the server or the client. You need to make the same data available for both during the initial render.
If nothing comes out to mind after this, you need to proceed by elimination. In case the error occurs on every page, it is likely to be a result of a misconfiguration of the server. For example, are you doing your own renderToString? Double check you didn't add an extra nested div in there, the string should be right inside the element you mount React on.
If that is not the case, try to extract one by one the components you are rendering, and you should be able to narrow down pretty quickly which is causing your issue.
Also keep in mind that you would need to restart your server every-time you make a change (unless you have a nodemon or similar config reloading the server-side code when you modify your source) for it to be applied!
As a last resort, you could potentially make your own diff between the server response and the client first render.
1) Open your console from your site, and paste the following:
console.log(document.documentElement.innerHTML)
2) Click on the Copy button, and paste that in a client.html file
3) Now run in your terminal:
curl YOUR_URL > server.html
4) It's likely the server will return you a minified version of your html, so you need to indent it in order to make it match with your client html, use something like this for that purpose.
5) Once you've done this, you can now run the actual diff in your terminal:
diff server.html client.html
This will list you every part of the files that differ between each other.
You can ignore diffs related to Javascript as the indenting will most likely be bad anyway, but concentrate on the html ones, where you might be able to spot differences and infer what is going wrong.
In your case, your translation system is likely to be the root cause of the issue. I would advice to follow more standard practices rather than next-i18next which seem pretty new and more likely to have problems. Someone else apparently also has an issue with SSR, and to be honest stuff like this is quite scary.
I know it can look a bit troublesome to setup, but here is my own i18n config which can be required either on the server or the client provided you specify a global variable to determine one which environment you are (here __BROWSER__).
import i18n from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { reactI18nextModule } from 'react-i18next'
i18n
.use(require(__BROWSER__ ? 'i18next-xhr-backend' : 'i18next-node-fs-backend'))
.use(LanguageDetector)
.use(reactI18nextModule)
.init({
fallbackLng: 'en',
ns: ['translations'],
defaultNS: 'translations',
interpolation: {
escapeValue: false,
},
react: {
wait: true,
},
backend: {
loadPath: __BROWSER__
? '/locales/{{lng}}/{{ns}}.json'
: require('path').join(__dirname, '/locales/{{lng}}/{{ns}}.json'),
},
})
export default i18n
You simply need to use the middleware, serve the locales from your server so the client can load them from xhr and have the I18nextProvider require the i18n instance. The full SSR docs are here.

I would start by looking at the html that get's to the browser(network tab in chrome devtools), then react is probably rendering client side anyway, so you can see the current DOM after the client side render and compare (go to elements tab in chrome devtools -> right click the html element and select "copy> copy outterHTML")
If that fails, you can try adding breakpoints in the browser inside react itself:
function canHydrateInstance # ReactDOMHostConfig.js
https://github.com/facebook/react/blob/c954efa70f44a44be9c33c60c57f87bea6f40a10/packages/react-dom/src/client/ReactDOMHostConfig.js
possibly relevant links to same kind of issue:
React 16 warning "warning.js:36 Warning: Did not expect server HTML to contain a <div> in <div>."
https://github.com/zeit/next.js/issues/5367

Related

window.dataLayer.push is working in local machine but not in the production environment

This is the logout logic, here the key logoutoverlay, is set in the local storage.
<div className="logout">
<NavLink href={propsData.userDataTopNavLinkList.logout} onClick={() => {
localStorage.removeItem("manage");
localStorage.removeItem("token");
localStorage.removeItem("auth");
localStorage.removeItem("AccountInfo");
localStorage.removeItem("successfullySet");
localStorage.removeItem("userImpression");
localStorage.removeItem("cartCnt");
localStorage.removeItem("callDuration");
localStorage.removeItem("customGeoIPInfo");
localStorage.removeItem("geoipInfo");
localStorage.setItem("logoutoverlay","form overlay")
}}> {dictionary.t("Logout", "LOGOUT")}</NavLink>
</div>
Here inside Main.js the local localStorage is checked for the key logoutoverlay
if(localStorage.getItem("logoutoverlay"))
{
logoutDataLayer(localStorage.getItem("logoutoverlay"));
localStorage.removeItem("logoutoverlay");
}
and inside dataLayerAnalytics.js the item is being pushed to the data layer
export const logoutDataLayer = (logoutplace) => {
let logoutDatalayer = {
"event": "loyaltyLogout",
"logoutContext": logoutplace
}
window.dataLayer.push(logoutDatalayer);
}
All of this is working fine on my local machine. But after I deployed it to the production environment the localStorage.removeItem("logoutPlace") inside the Main.js is not getting executed and the code can't reach localStorage.removeItem("logoutPlace") as well. That's why the item "logoutoverlay" doesn't get removed from the local storage.
And since I am unable to replicate this issue on my local system, I can't even debug the code.
Introduction
Your problem statement is that
localStorage.removeItem("logoutoverlay");
is not executed on prod, but it is successfully executed on your dev env. Since we do not have your source-code we cannot tell you for sure what the problem is, but we can tell you how you can find out what the problem is. This answer focuses on the method of finding out what the issue is.
How to debug the client-side of prod
If your source-code is available on prod, then you can debug your client-side code, written in Javascript via the Sources tab of your browser's dev tools. You can reach it via:
right-click anywhere on the page -> click on inspect/inspect element (the text varies accross browsers) -> dev tools appear, usually with the Elements tab being shown by default -> click on Sources -> find Main.js and click on that
Now, in your Main.js you can click on the left-most edge of the source-code's lines to put in breakpoints and from there on you can debug.
If the client-side code is minified on prod, then you have several options to proceed. You could download the minified script into your dev env and temporarily use that to debug. Or, you can implement a testing mode on prod, which would allow you to load and use the original, not minified js code, which would allow you to debug your prod's client-side code in the browser.
Make sure you compare apples with apples
It is certainly possible that your server has a different version of your client-side code, which behaves differently. Make sure that you have equivalent source-code on the server as in your dev env, since, if these are different, then it is unsurprising that they behave differently. To do so, you need to:
clear your browser cache or open the page in incognito mode in order to avoid being misled by old file versions being loaded into your browser either on server or client-side
clear your server cache if you have one, since that prevents new code from being used for a while
download Main.js from the server and study it, possibly comparing it to the Main.js you have on the dev environment
if you don't use a version controller, then it is high time you start using one. If you have a version controller, then, using it look at the commit hash of the latest commit and the branch being in use at both, maybe pulling/pushing in order to ensure that the versions are the same
Check user state on client-side and server-side
This is the problematic section of your code:
if(localStorage.getItem("logoutoverlay"))
{
logoutDataLayer(localStorage.getItem("logoutoverlay"));
localStorage.removeItem("logoutoverlay");
}
Maybe on your server's localStorage you do not have logoutoverlay. Maybe you have logged in to your server before that value was tracked and never logged in since. If it's just a matter of user state, then basically the problem is just coping with differences of versions.
Be careful about user states when you deploy session logic changes
You implement some logout logic which assumes some values inside localStorage. What if that value is not inside localStorage, because the user logged in before the change that creates it was deployed. The last thing you want is to have some broken user sessions, so make sure that your work covers the case when users logged in before your deploy and their session still exists. If during deployment all sessions are destroyed, then this is not an actual concern.

Changing Brackets Shell Name results in WebSocket connection Error

I am trying to build an app using the Brackets shell. More specifically I am trying to build a custom code editor for a project so instead of starting from scratch I am modifying Brackets.
So far I have been able to work through all issues until I got to the Brackets Shell. I want to be able to install my app beside brackets, so it has to have a different name and separate preferences. I followed this guide on how to rename a Brackets Shell app. Here are the files I changed:
Gruntfile.js – change the build name
appshell/config.h – Change the app-name for windows and osx
appshell_config.gypi – Change the app-name as well
After running grunt setup and grunt build-mac my app launches and seems to work fine. I can change preferences in my app without affecting the original Brackets app (because they have different Application Support directories). I did not notice any issues until I opened the console where I saw the following error:
WebSocket connection to 'ws://localhost:50642/' failed: HTTP Authentication failed;
no valid credentials available NodeConnection.js:84
So I tried changing my apps name back to Brackets in all three files, and the issue goes away. My guess is somewhere in the code the app it is still trying to connect to the original app named Brackets. I'm guessing there is a 4th config file I need to change, but I am not familiar enough with Brackets to be able to locate that file. Without the connection Live Preview and eslint don't work.
I have tried inserting console.trace to try and reverse engineer how the Node Connection works between the Brackets Shell and the Brackets source code itself, but that didn't help much. Does anyone know how to change the name of Brackets Sheel without breaking NodeConnection at runtime?
I also tried searching for processes on port 50642 and the server is running.
You need to modify Node Core
Brackets Shell is hardwired to reject any call that is not from apps named Brackets. Open file brackets/appshell/node-core/Server.js. As of answering this question, you need to change line 205. Just in case that is different in the future you can find the commit I'm looking at here.
Here is what is causing the issue:
wsServer = new WebSocket.Server({
server: httpServer,
verifyClient : function (info, callback) {
// Accept connections originated from local system only
// Also do a loose check on user-agent to accept connection only from Brackets CEF shell
if (info.origin === "file://" && info.req.headers["user-agent"].indexOf(" Brackets") !== -1) {
callback(true);
} else {
// Reject the connection
callback(false);
}
}
});
The problem is info.req.headers["user-agent"].indexOf(" Brackets"). As you can see it rejects any connections that are not from Brackets. Rename brackets to whatever your app is called.
Make sure you format the name correctly
If your app has a space it in (eg. New Brackets), then you would remove the space when checking the user-agent. In this example you would check that the user-agent like so: info.req.headers["user-agent"].indexOf(" NewBrackets").
Congrats! You have built New Brackets.

How to force client reload after deployment?

I'm using the MEAN stack (mongo, express, angular and node). I'm deploying relatively frequently to production...every couple of days. My concern is that I'm changing the client side code and the API at times and I would rather not have to ensure backwards compatibility of the API with previous versions of the client code.
In such a scenario, what is the most effective way of ensuring that all clients reload when I push to production? I have seen that Evernote for example has a pop-up that says something along the lines of please reload your browser for the latest version of Evernote. I would like to do something similiar...do I need to go down the path of socket.io or sock.js or am I missing something simple and there is a simpler way to achieve this?
Update:
AppCache was deprecated summer 2015 so the below is no longer the best solution. The new recommendation is to use Service Workers instead. However, Service Workers are currently still experimental with sketchy (read: probably no) support in IE and Safari.
Alternatively, many build tools now seamlessly incorporate cache-busting and file "versioning" techniques to address OPs question. WebPack is arguably the current leader in this space.
This might be a good use case for using HTML5's AppCache
You'd probably want to automate some of these steps into your deployment scripts, but here is some code you might find useful to get you started.
First, create your appcache manifest file. This will also allow you to cache resources in the client's browser until you explicitly modify the appcache manifest file's date.
/app.appcache:
CACHE MANIFEST
#v20150327.114142
CACHE:
/appcache.js
/an/image.jpg
/a/javascript/file.js
http://some.resource.com/a/css/file.css
NETWORK:
*
/
In app.appcache, the comment on line #v20150327.114142 is how we indicate to the browser that the manifest has changed and resources should be reloaded. It can be anything, really, as long as the file will look different to the browser from the previous version. During deployment of new code in your application, this line should be modified. Could also use a build ID instead.
Second, on any pages you want to use the appcache, modify the header tag as such:
<html manifest="/app.appcache"></html>
Finally, you'll need to add some Javascript to check the appcache for any changes, and if there are, do something about it. Here's an Angular module. For this answer, here's a vanilla example:
appcache.js:
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
// Browser downloaded a new app cache.
// Swap it in and reload the page to get the latest hotness.
window.applicationCache.swapCache();
if (confirm('A new version of the application is available. Would you like to load it?')) {
window.location.reload();
}
}
else {
// Manifest didn't changed. Don't do anything.
}
}, false);
Alternatively, if AppCache won't work for your situation, a more ghetto solution would be to create a simple API endpoint that returns the current build ID or last deployment date-time. Your Angular application occasionally hits this endpoint and compares the result to it's internal version, and if different, reloads itself.
Or, you may consider a live-reload script (example), but, while very helpful in development, I'm not sure how good of an idea it is to use live/in-place-reloading of assets in production.
I will tell you my problem first then I will recommend a tentative solution. I wanted to force my user to log out and then log in when a production build is been deployed. At any point in time, there will be two versions of software deployed on production. A version which software which FE knows and a version which Backend knows. Most of the time they would be the same. At any point in time if they go out of sync then we need to reload the client to let the client know that a new production build has been pushed.
I am assuming 99.99% of the time the backend would have the knowledge of the latest version of the deployed software on production.
following are the two approaches which I would love to recommend:-
The backend API should always return the latest version of the software in the response header. On the frontend, we should have a common piece of code that would check if the versions returned by the API and that present on the FE are the same. if not then reload.
Whenever a user logs in. the BE should encode the latest software version in the JWT. And the FE should keep sending this as a bearer token along with every API request. The BE should also write a common interceptor for every API request. which would compare the software version in the JWT received from the API request and the
Maybe you can add hash to your client code file name. eg app-abcd23.js.
So the browser will reload the file instead of get it from cache. or you can just add the hash to url.eg app.js?hash=abcd23 but some browser may still use the cached version.
i know rails has assets-pipline to handle it, but i am not familiar with MEAN stack. there should be some package in npm for that purpose.
And i dont think it is really necessary to use socket.io if you want to notify the user their client code is out of date. you can define your version in both html meta tag and js file,if mismatch, show a popup and tell the user to refresh.
Try to limit your js/files to expire within smaller periodic time, ie: 1 days.
But in case you want something that pop-out and tell your user to reload (ctrl+f5) their browser, then simply make a script that popup that news if you just changed some of your files, mark the ip/session who have just reload/told to reload, so they will not be annoyed with multiple popup.
I was facing the same problem recently. I fixed this by appending my app's build number with my js/css files. All my script and style tags were included by a script in a common include files so it was trivial to add a 'build number' at the end of the js/css file path like this
/foo/bar/main.js?123
This 123 is a number that I keep track of in my same header file. I increment it whenever I want the client to force download all the js files of the app. This gives me control over when new versions are downloaded but still allows the browser to leverage cache for every request after the first one. That is until I push another update by increment the build number.
This also means I can have a cache expiry header of however long I want.
Set a unique key to local storage during the build process
I am using react static and loading up my own data file, in there i set the ID each time my content changes
Then the frontend client reads the key with from local storage
(if the key does not exist it must be the first visit of the browser)
if the key from local storage does not match it means the content has changed
fire line below to force reload
window.replace(window.location.href + '?' + key)
in my case i had to run this same line again a second latter
like
setTimeout( (window.replace(window.location.href + '?' + key))=> {} , 1000)
full code below:
const reloadIfFilesChanged = (cnt: number = 0, manifest: IManifest) => {
try {
// will fail if window does not exist
if (cnt > 10) {
return;
}
const id = localStorage.getItem('id');
if (!id) {
localStorage.setItem('id', manifest.id);
} else {
if (id !== manifest.id) {
// manifest has changed fire reload
// and set new id
localStorage.setItem('id', manifest.id);
location.replace(window.location.href + '?' + manifest.id);
setTimeout(() => {
location.replace(window.location.href + '?' + manifest.id + '1');
}, 1000);
}
}
} catch (e) {
// tslint:disable-next-line:no-parameter-reassignment
cnt++;
setTimeout(() => reloadIfFilesChanged(cnt, manifest), 1000);
}
};

How to get my 404 page to show after upgrade of Sails.js to 0.10.x?

I've upgraded my Sails.js app to 0.10.x and now when I point my browser at a non-existent route such as http://localhost:1337/notfound instead of my views/404.jade being served up I just get a bit of JSON
{
"status": 404
}
I built a default new sails app sails new dummy --template=jade just to compare and contrast with what I have in my existing app, and the only obvious difference I see is that in dummy/config/ there is a file called http.js
I've copied that file over to my app but it's made no difference.
I've also ensured that the tasks in dummy/tasks are identical to my own app's tasks.
In dummy/config/routes.js it says
Finally, if those don't match either, the default 404 handler is triggered.
See config/404.js to adjust your app's 404 logic.
Which is obviously out of date as 0.10.x apps use the api/responses mechanisms.
So I am at rather a loss as to how to get this to work.
I am using 0.10.0-rc8 (and I have confirmed that this is the same in my dummy app as well as my actual app)
Well I've fixed this but I have no idea why it was happening.
To fix it I created a new project as per the method described in my question, but with the same name as my existing project, then, file-by-file, I painstakingly moved across everything specific to my app, taking care to leave in place anything new generated by sails.
Once I'd done that I ran my tests - all passed - and then sails lift and tried it, and yay, everything worked and I got my 404 error page back.
I committed my changes and then carefully picked through a comparison of what had changed.
Alas nothing at all stands out, so, while I have solved my problem, I have no idea what the original cause was.
From the section in the migration guide on "Custom Responses":
In v0.10, you can now generate your own custom server responses. See
here to learn how. Like before, there are a few that we automatically
create for you. Instead of generating myApp/config/500.js and other
.js responses in the config directory, they are now generated in
myApp/api/responses/. To migrate, you will need to create a new v0.10
project and copy the myApp/api/responses directory into your existing
app. You will then modify the appropriate .js file to reflect any
customization you made in your response logic files (500.js,etc).
Basically, v0.10.x gives you more freedom in how things like res.notFound, res.serverError and even res.ok() (success response) work, but you need to copy over the new api/responses folder to migrate.
I had the same issue but was using 0.9.x. Probably a better solution but I outputted a view instead of JSON in all cases.
Update config/404.js to replace res.json() with res.view():
if (err) {
//return res.json(result, result.status); }
return res.view('404') // <-- output the 404 view instead
}
Then, just make sure in your routes.js file it will redirect the /404. Place the following at the bottom of your routes.js file:
'/*': {
view: '*'
},

How can I edit on my server files without restarting nodejs when i want to see the changes?

I'm trying to setup my own nodejs server, but I'm having a problem. I can't figure out how to see changes to my application without restarting it. Is there a way to edit the application and see changes live with node.js?
Nodules is a module loader for Node that handles auto-reloading of modules without restarting the server (since that is what you were asking about):
http://github.com/kriszyp/nodules
Nodules does intelligent dependency tracking so the appropriate module factories are re-executed to preserve correct references when modules are reloaded without requiring a full restart.
Check out Node-Supervisor. You can give it a collection of files to watch for changes, and it restarts your server if any of them change. It also restarts it if it crashes for some other reason.
"Hot-swapping" code is not enabled in NodeJS because it is so easy to accidentally end up with memory leaks or multiple copies of objects that aren't being garbage collected. Node is about making your programs accidentally fast, not accidentally leaky.
EDIT, 7 years after the fact: Disclaimer, I wrote node-supervisor, but had handed the project off to another maintainer before writing this answer.
if you would like to reload a module without restarting the node process, you can do this by the help of the watchFile function in fs module and cache clearing feature of require:
Lets say you loaded a module with a simple require:
var my_module = require('./my_module');
In order to watch that file and reload when updated add the following to a convenient place in your code.
fs.watchFile(require.resolve('./my_module'), function () {
console.log("Module changed, reloading...");
delete require.cache[require.resolve('./my_module')]
my_module = require('./my_module');
});
If your module is required in multiple files this operation will not affect other assignments, so keeping module in a global variable and using it where it is needed from global rather than requiring several times is an option. So the code above will be like this:
global.my_module = require ('./my_module');
//..
fs.watchFile(require.resolve('./my_module'), function () {
console.log("Module changed, reloading...");
delete require.cache[require.resolve('./my_module')]
global.my_module = require('./my_module');
});
Use this:
https://github.com/remy/nodemon
Just run your app like this: nodemon yourApp.js
There should be some emphasis on what's happening, instead of just shotgunning modules at the OP. Also, we don't know that the files he is editing are all JS modules or that they are all using the "require" call. Take the following scenarios with a grain of salt, they are only meant to describe what is happening so you know how to work with it.
Your code has already been loaded and the server is running with it
SOLUTION You need to have a way to tell the server what code has changed so that it can reload it. You could have an endpoint set up to receive a signal, a command on the command line or a request through tcp/http that will tell it what file changed and the endpoint will reload it.
//using Express
var fs = require('fs');
app.get('reload/:file', function (req, res) {
fs.readfile(req.params.file, function (err, buffer) {
//do stuff...
});
});
Your code may have "require" calls in it which loads and caches modules
SOLUTION since these modules are cached by require, following the previous solution, you would need a line in your endpoint to delete that reference
var moduleName = req.params.file;
delete require.cache[moduleName];
require('./' + moduleName);
There's a lot of caveats to get into behind all of this, but hopefully you have a better idea of what's happening and why.
What's “Live Coding”?
In essence, it's a way to alter the program while it runs, without
restarting it. The goal, however, is to end up with a program that
works properly when we (re)start it. To be useful, it helps to have an
editor that can be customized to send code to the server.
Take a look: http://lisperator.net/blog/livenode-live-code-your-nodejs-application/
You can also use the tool PM2. Which is a advanced production process tool for node js.
http://pm2.keymetrics.io/
I think node-inspector is your best bet.
Similar to how you can Live Edit Client side JS code in Chrome Dev tools, this utilizes the Chrome (Blink) Dev Tools Interface to provide live code editing.
https://github.com/node-inspector/node-inspector/wiki/LiveEdit
A simple direct solution with reference to all answers available here:
Node documentation says that fs.watch is more efficient than fs.watchFile & it can watch an entire folder.
(I just started using this, so not really sure whether there are any drawbacks)
fs.watch("lib", (event_type, file_name) => {
console.log("Deleting Require cache for " + file_name);
delete require.cache[ require.resolve("./lib/" + file_name)];
});

Categories

Resources