Why component is called twice in Next.js? - next.js

This is my pages/index.js of my next.js project
const Index = () => {
console.log('Index Component Called');
return (
<div>Hello</div>
)
}
export default Index;
The console log function is called twice on CLIENT side and one time on next.js node SERVER

I guess because Pages in next.js are server side rendered (or pre-rendered).
So in this case when next.js is rendering your page(server side), the script will console.log("Index Component Called") then on your frontend react it's hydrating so all the code coming from the server should be executed again.
In next.js you can execute Server Side code inside getInitialProps, and in pages only not components.

Related

What are the differences between Next.js 13 server components and client components?

I'm trying to understand the differences between Next.js 13 server components and client components, and I have a few questions:
If the server component doesn't have the ability to access hooks, what is the benefit of using it, and how can users interact with it?(state, reactivity)
Are both client components and server components SEO-friendly, or is one better for SEO than the other?
Can we hide API endpoints with client components like we can with server components?
A sample situation:
app/abc/page.tsx
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
Error:
"useState" is not allowed in Server Components.ts(71001)
All Next components have ability to access hooks. But for example useEffect will be executed only in client side.
Server component better for SEO. Because for search engines SSR page looks like static HTML already generated on server.
Hide like SSR - no. Because components rendered in server side and all method required for render called on server. But user requests will be on client side and with SSR and with CSR. But Next offers a built-in proxy function
Yes, with app dir exists some problems.
This approach allows you to make SSR component without getServerSide props and with some others new features. But this is beta and if for you it creates some problem, dont use app dir, use getServerSideProps

Lazily added routes are not accessible by url

When component introduces new route for its router-view it is working while going trough the app, but once you refresh or try to access url directly it does not load. I assume that it is because of the fact that component adding routes did not add them yet.
But I would expect that router would parse url by its segments, match parent component, loads it (which introduces child routes) and then continue with next segment. Or something similar.
Is there a way how to achieve routing added lazily? So each loaded module introduce its part (module) of a router? But in same time they can be accessed by url?
Thanks
Here is reproduced issue: https://codesandbox.io/s/vue-3-router-lazy-route-5opufo
Click on Admin link and then settings, it works, try to access admin/settings by url, it does not load settings content.
EDIT:
Here what I expect https://stackblitz.com/edit/angular-vwnzjg
ATTENTION it fails on stackblitz, but you can download the project, install dependencies and try it yourself to see that it works correctly.
After start dev server, navigate directly to http://localhost:4200/customers/profile as you can see, it works even the fact that the router part targeted is loaded lazily in Customers module.
When you click on admin and then click on settings, the code run follows this scenario:
Click admin -> router resolved (found admin component) -> admin component setup -> you dynamic add settings router
-> click settings -> router resolved (found settings component) -> load settings component successfully
But when you reload the browser on the /admin/settings route, the scenario will be:
Reload page '/admin/settings' -> router resolved (found admin component but NOT the settings component because admin component's setup code is not run yet)
-> only load admin component
In this scenario, the admin component's setup code will be run after the router is resolved. So the dynamic adding router code is not run at that time and the router can NOT resolve the settings component
Solution
Move your dynamic adding router code to beforeEach navigation guard so it can be run every time the router resolved
router.beforeEach((to, from) => {
const hasRoute = router.hasRoute(to.name || "");
if (to.path === "/admin/settings" && !router.hasRoute(to.name || "")) {
router.addRoute("admin", {
path: "settings",
name: "settings",
component: { template: "<div>AdminSettings</div>" }
});
// Don't forget to return the router here to make sure the router will be correctly resolved
return { name: "settings" };
}
});

Firebase Nuxt Does Not Recognize 'location' Object When Trying to Run The Whole App With Firebase Emulator

My firebase Nuxt app was recently converted to SSR from SPA mode. It was all working fine in SPA mode but when I tried to convert it, it generated a lot of errors. I tried to solve them one by one and I'm stuck with the error ReferenceError 'location' was not defined. I want to run my emulator because I want to test my other functions if it is running completely in SSR mode.
import firebaseTmp from "firebase/app";
import firebaseErrorsJa from "~/plugins/firebaseErrorsJa";
import "firebase/storage";
import "firebase/firestore";
import 'firebase/auth';
import 'firebase/functions';
const config = process.env.firebaseConfig;
if (!firebaseTmp.apps.length) {
firebaseTmp.initializeApp(config);
}
const db = firebaseTmp.firestore();
const functions = firebaseTmp.functions();
const firebase = firebaseTmp;
const firestore = firebaseTmp.firestore();
const storage = firebaseTmp.storage();
const auth = firebaseTmp.auth();
const firestoreTimestamp = firebaseTmp.firestore.Timestamp;
const serverTimestamp = firebaseTmp.firestore.FieldValue.serverTimestamp();
const firebaseErrors = firebaseErrorsJa;
if (location.hostname === "localhost") {
db.settings({
host: "localhost:8000",
ssl: false
});
functions.useEmulator("localhost", 5001);
auth.useEmulator('http://localhost:9099/');
}
export { db, firebase, firestore, auth, storage, firestoreTimestamp, serverTimestamp, functions, firebaseErrors }
I imported almost all libraries but still it does not work.
TAKE NOTE: This only happens when it is in SSR mode. Does this mean that location does not work in SSR mode?
I tried to take away the chunk of code that has 'location' in it. It works perfectly well locally but when I try to run my other functions, it generates CORS error. It accesses the link being used when we deploy our functions.
https://us-central1-talkfor-dev.cloudfunctions.net/v1-auth-updateUser
This is the link that was shown in the console
What I expected is that us-central1-talkfor-dev.cloudfunctions.net will be localhost:5000 since we are using the local development.
Do you have any Idea why is it like this?
Alex you understand that SSR mode is trying to render the website on the server. This can be your local running server instance, that means there is no window object for it to access location variable. Location is only accessible inside the browser. The SSR context is not in the browser, hence makes total sense.
Window.location here states.
Server vs Browser environments here states that
Because you are in a Node.js environment... You do not have access to the window or document objects... You can however use window or document by using the beforeMount or mounted hooks.
.
Why don't you try either of these,
use these beforeMount and mounted hooks like this.
beforeMount () {
window.alert('hello');
}
mounted () {
window.alert('hello');
}
using the process.client environment variable
if (process.client) {
// Do your stuff here
// I think this is what you are looking for.
}

Is it possible to access a real API in React Storybook

I am using Storybook to test my React UI components.
However, when I get to a point where my Action makes an Axios request, I get a 404 response.
Below is the code used in a react action file:
assume the axios instantiation, thunk implementation and action definitions.
getDelayedThunkRes: () => {
return (dispatch) => {
dispatch(delayedResActions.getInitialRes());
axios.get("/test").then(success => {
console.log(success);
}).then(err => {
console.log(err);
})
}
}
localhost:8080 is my real server that I want to connect to. Obviously it should throw me an error because my storybook is running on 9009. How can I connect the two?
Note, it works for my Create React App. Create React App package gives a provision to proxy all the calls to a server using "proxy" field in package.json
Are there any similar tools in Storybook, or is Storybook supposed to be used solely with static mock data?
Alright, I found an amazing post on how to create a middleware for React storybook for APIs
https://medium.com/#wathmal/adding-a-router-middle-ware-to-react-storybook-4d2585b09fc
Please visit the link. The guy deserves the due credit.
Here is my implementation of it in ES5 (somehow Storybook middleware is unable to transpile):
create this middleware.js inside .storybook directory:
const express = require('express');
const bodyParser = require('body-parser');
const expressMiddleWare = function(router) {
router.use(bodyParser.urlencoded({extended: false}));
router.use(bodyParser.json());
router.get('/test', function(req, res) {
res.send('Hello World!');
res.end();
});
}
module.exports = expressMiddleWare
Caveat: You will have to restart Storybook every time you make a change in the middleware.
With this, I am able to make a call from my react actions.
Next, I will try to implement express HTTP proxy middleware to redirect these storybook middleware calls to my real express server.
Edit 1:
The new technique seems to be using decorators, especially with stroybook-addon-headless.
Storybook add on for setting server urls
https://github.com/ArrayKnight/storybook-addon-headless
I am yet to try

history.push in react router fires new request, nginx return 404

I read a lot of questions about 'history.push nginx react-router' but I didn't find the solution for the problem I'm facing.
The problem is, when I log in my application in my Login component I do something like this after the ajax call is executed:
cb = (res) => {
if (res.token!== null && res.token !== undefined){
localStorage.setItem("token",res.token);
localStorage.setItem("expiresIn",res.expiresIn);
localStorage.setItem("email", res.email);
**history.push('/home/0/0');**
}else{this.setState({errorTextMail:"wrong mail or wrong password!"})}}
Locally it works fine but in the console the history.push('/home/0/0') fires a request that nginx doesn't handle and return 404, but in my browser the app log me in and I don't see the error.
Instead when I build the app and put the build folder under nginx to serve static files, when I try to login it show me the 404 page. Then if I refresh the page (removing the /home/0/0 from the url) it works fine, recognize the token and log me in and I can browse other route of the app.
I was supposed that history.push would have been handled by react-router, just changing the component mapped by the appropriate <Route path="/home/:idOne/:idTwo" component={Home} /> component and not firing a new request to nginx.
My question is, there is a way to switch compoent when the cb function return my token instead of using history.push('/home/0/0')?
Note the history object is defined this way
import history from './history';
and the history.js's content is :
import createHistory from 'history/createBrowserHistory'
export default createHistory({
forceRefresh: true
})
Thanks for any suggestion.

Resources