NextJS localstorage is not defined [duplicate] - redux

This question already has answers here:
In React and Next.js constructor, I am getting "Reference Error: localstorage is not defined"
(6 answers)
localStorage in static NextJS site: ReferenceError: localStorage is not defined
(3 answers)
Closed 10 months ago.
How can i fix this? i'm trying to almacenate my cartItems state in the localStorage but nextJs throw me localstorage is not defined error.
const initialState = {
cartItems: localStorage.getItem("cartItems")
? JSON.parse(localStorage.getItem("cartItems"))
: [],
cartTotalQuantity: 0,
cartTotalAmout: 0,
};

localStorage is defined only in browsers (of course!)
in Next.js, your code is run both in the browser and the server.
You can safely initialize and use your state in useEffect hook like this
function MyComponent() {
const [myState, setMyState] = React.useState({ cartItems: [], cartTotalQuantity: 0, cartTotalAmount: 0 });
// This code runs after the component mounted
React.useEffect(() => {
const cartItemsString = localStorage.get('cartItems');
if (cartItemsString) {
setMyState((prev) => ({ ...prev, cartItems: JSON.parse(cartItemsString) }))
}
}, [])
return <>YOUR JSX GOES HERE</>
}

Related

Next.js with Firebase Remote Config

I was trying to integrate Google's Firebase Remote config into my Next.js app.
When following Firebase's docs, and just inserted the functions directly into my component's code block, like so:
const remoteConfig = getRemoteConfig(app);
I keep getting the following error when following their documentation:
FirebaseError: Remote Config: Undefined window object. This SDK only supports usage in a browser environment.
I understand that it happens since Nextjs is rendered server-side, so there's no window object yet, so here's my solution:
import {
fetchAndActivate,
getRemoteConfig,
getString,
} from 'firebase/remote-config';
const Home: NextPage<Props> = (props) => {
const [title, setTitle] = useState<string | null>('Is It True?');
useEffect(() => {
if (typeof window !== 'undefined') {
const remoteConfig = getRemoteConfig(app);
remoteConfig.settings.minimumFetchIntervalMillis = 3600000;
fetchAndActivate(remoteConfig)
.then(() => {
const titleData = getString(remoteConfig, 'trueOrFalse');
setTitle(titleData);
})
.catch((err) => {
console.log(err);
});
}
});
return <h1>{title}</h1>}
Basically, the important part is the if statement that checks if the window object exists, then it execute the Remote Config functions according to Firebase documents.
Also, it worked outside a useEffect, but I think that's probably a bad idea to leave it outside, maybe even it should have a dependency, can't think of one at the moment.

React Query - useQuery callback dependent on route parameter? [duplicate]

When page is refreshed query is lost, disappears from react-query-devtools.
Before Next.js, I was using a react and react-router where I would pull a parameter from the router like this:
const { id } = useParams();
It worked then. With the help of the, Next.js Routing documentation
I have replaced useParams with:
import { usePZDetailData } from "../../hooks/usePZData";
import { useRouter } from "next/router";
const PZDetail = () => {
const router = useRouter();
const { id } = router.query;
const { } = usePZDetailData(id);
return <></>;
};
export default PZDetail;
Does not work on refresh. I found a similar topic, but manually using 'refetch' from react-query in useEffects doesn't seem like a good solution. How to do it then?
Edit
Referring to the comment, I am enclosing the rest of the code, the react-query hook. Together with the one already placed above, it forms a whole.
const fetchPZDetailData = (id) => {
return axiosInstance.get(`documents/pzs/${id}`);
};
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {});
};
Edit 2
I attach PZList page code with <Link> implementation
import Link from "next/link";
import React from "react";
import TableModel from "../../components/TableModel";
import { usePZSData } from "../../hooks/usePZData";
import { createColumnHelper } from "#tanstack/react-table";
type PZProps = {
id: number;
title: string;
entry_into_storage_date: string;
};
const index = () => {
const { data: PZS, isLoading } = usePZSData();
const columnHelper = createColumnHelper<PZProps>();
const columns = [
columnHelper.accessor("title", {
cell: (info) => (
<span>
<Link
href={`/pzs/${info.row.original.id}`}
>{`Dokument ${info.row.original.id}`}</Link>
</span>
),
header: "Tytuł",
}),
columnHelper.accessor("entry_into_storage_date", {
header: "Data wprowadzenia na stan ",
}),
];
return (
<div>
{isLoading ? (
"loading "
) : (
<TableModel data={PZS?.data} columns={columns} />
)}
</div>
);
};
export default index;
What you're experiencing is due to the Next.js' Automatic Static Optimization.
If getServerSideProps or getInitialProps is present in a page, Next.js
will switch to render the page on-demand, per-request (meaning
Server-Side Rendering).
If the above is not the case, Next.js will statically optimize your
page automatically by prerendering the page to static HTML.
During prerendering, the router's query object will be empty since we
do not have query information to provide during this phase. After
hydration, Next.js will trigger an update to your application to
provide the route parameters in the query object.
Since your page doesn't have getServerSideProps or getInitialProps, Next.js statically optimizes it automatically by prerendering it to static HTML. During this process the query string is an empty object, meaning in the first render router.query.id will be undefined. The query string value is only updated after hydration, triggering another render.
In your case, you can work around this by disabling the query if id is undefined. You can do so by passing the enabled option to the useQuery call.
export const usePZDetailData = (id) => {
return useQuery(["pzs", id], () => fetchPZDetailData(id), {
enabled: id
});
};
This will prevent making the request to the API if id is not defined during first render, and will make the request once its value is known after hydration.

Use tw elements in a NextJS project

I'm trying to use tw-elements in a nodejs project. If I follow their documentation and just import tw-elements in my _app, I get this error:
ReferenceError: document is not defined
I found a stackoverflow response that said to put this at the start of the index.min.js file of tw-elements:
if (typeof window == "undefined")return;
I did and the error disappeared, but the library still won't work. Any ideas?
First, add Tailwind Elements using these NPM steps here.
Here is how to get it to work with Nextjs:
First step is to add this code to your _app.js file:
useEffect(() => {
const use = async () => {
(await import('tw-elements')).default;
};
use();
}, []);
Like this for example:
export default function App({ Component, pageProps }) {
useEffect(() => {
const use = async () => {
(await import('tw-elements')).default;
};
use();
}, []);
return (
Make sure you add import { useEffect } from "react"; to the top of _app.js.
It’s also important that you’re not importing Tailwind Elements anywhere else expect for the _app.js file.
Tailwind Elements should now be working!
I was facing the same issue. I followed Tyrell Curry's answer but It encountered type not found error because I was using typescript.
Unfortunately the type definitions were missing for tailwind-elements library.
I made a little change it the function so that type check have to be avoided by using as any.
useEffect(() => {
const use = async () => {
(await import("tw-elements" as any)).default;
};
use();
}, []);

Getting this error "Invariant failed: A state mutation was detected inside a dispatch, in the path: todoReducer.1."

I tried everything like spread operator but nothing works.
Here is my reducer
//state is an array of objects.
const initialState = [
{taskName: "kkkkk", isEdit: false},
]
export const todoReducer = (state=initialState, action) =>{
switch(action.type){
case 'add' :
const temp=
{
taskName: action.payload.taskName,
isEdit: action.payload.isEdit
}
state.push(temp);
return {state}
default: return state
}
}
The error message indicates that you are using Redux Toolkit - that is very good. The problem is that you are not using createSlice or createReducer and outside of those, in Redux you are never allowed to assign something to old state properties with = or call something like .push as it would modify the existing state.
Use createSlice instead:
const initialState = [
{taskName: "kkkkk", isEdit: false},
]
const slice = createSlice({
name: 'todos',
reducers: {
add(state, action) {
state.push(action.payload)
}
}
})
export const todoReducer = slice.reducer;
// this exports the auto-generated `add` action creator.
export const { add } = slice.actions;
Since the tutorial you are currently following seems to be incorporating both modern and completely outdated practices, I would highly recommend you to read the official Redux Tutorial instead, which shows modern concepts.

GatsbyJS with Firebase - WebpackError: ReferenceError: IDBIndex is not defined

I'm received error with gatsby develop. It's very similar to this one: https://github.com/firebase/firebase-js-sdk/issues/2222, but I'm received error with gatsby develop, not with gatsby build. I did a lot of research but I can't find working solution.
At first I had a problem with gatsby build, like in this post: https://github.com/firebase/firebase-js-sdk/issues/2222, but I resolved it with custom onCreateWebpackConfig(you can find it below).
Stack:
- Gatsby
- Firebase(error probably with that)
- Redux
I'm also delete .cache and node_modules and install everything again, but it didn't work.
Error:
There was an error compiling the html.js component for the development server.
See our docs page on debugging HTML builds for help https://gatsby.dev/debug-html ReferenceError: IDBIndex is not defined
]);
86 |
> 87 | proxyRequestMethods(Index, '_index', IDBIndex, [
| ^
88 | 'get',
89 | 'getKey',
90 | 'getAll',
WebpackError: ReferenceError: IDBIndex is not defined
- idb.mjs:87 Module../node_modules/idb/lib/idb.mjs
node_modules/idb/lib/idb.mjs:87:1
- index.esm.js:1 Module../node_modules/#firebase/installations/dist/index.esm.js
node_modules/#firebase/installations/dist/index.esm.js:1:1
- index.esm.js:1 Module../node_modules/#firebase/analytics/dist/index.esm.js
node_modules/#firebase/analytics/dist/index.esm.js:1:1
- index.esm.js:1 Module../node_modules/firebase/analytics/dist/index.esm.js
node_modules/firebase/analytics/dist/index.esm.js:1:1
- index.ts:1 Module../src/firebase/index.ts
src/firebase/index.ts:1:1
- index.esm.js:32 emit
node_modules/#firebase/analytics/dist/index.esm.js:32:1
My gatsby-node file:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
}
};
My firebase dependencies:
"#firebase/firestore-types": "^1.10.1",
"firebase": "^7.13.1",
"firebase-admin": "^8.10.0",
"firebase-functions": "^3.5.0",
"firebase-tools": "^7.16.1",
Firebase index file:
import firebase from 'firebase/app';
import 'firebase/firestore';
import 'firebase/auth';
import 'firebase/storage';
import 'firebase/analytics';
const firebaseConfig = {...};
firebase.initializeApp(firebaseConfig);
export const firestore = firebase.firestore();
export const auth = firebase.auth();
export const storage = firebase.storage();
Project repo: https://github.com/olafsulich/Projecty
Post on Github issues: https://github.com/firebase/firebase-js-sdk/issues/2946
Thanks in advance.
The following snippet will only work on build environment because of your condition (stage === 'build-html'):
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
}
};
Remove it and use it like this:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
actions.setWebpackConfig({
externals: getConfig().externals.concat(function(context, request, callback) {
const regex = /^#?firebase(\/(.+))?/;
if (regex.test(request)) {
return callback(null, `umd ${request}`);
}
callback();
}),
});
};
Thank's a lot! It's working only on gatbsy develop, but now when I
want to build project, I get an error - TypeError: Cannot read
property 'concat' of undefined. You know how to solve it?
Regarding the new issue, you can follow a workaround in this topic, This is a common error in third-party modules in Gatsby when they try to reach a DOM element (usually window) that is not already defined when the app builds. So, you need to wait until window is defined. You can achieve this in two ways:
Instancing your firebase with a condition like this:
import firebase from '#firebase/app';
import '#firebase/auth';
import '#firebase/firestore';
import '#firebase/functions';
const config = {
... firebase config here
};
let instance;
export default function getFirebase() {
if (typeof window !== 'undefined') {
if (instance) return instance;
instance = firebase.initializeApp(config);
return instance;
}
return null;
}
Note the if (typeof window !== 'undefined') statement
By ignoring firebase module in you webpack configuration like shows their docs. In your gatsby-node.js:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /bad-module/,
use: loaders.null(),
},
],
},
})
}
}
Replace bad module for firebase (or the package/folder name in node_modules). Leave the slashes since test is a regular expression rule
This snippet replaces your previous one that seems to throw an error in concat() function.
For those who wants to try the concat() resolution, this will be helpful too:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat((context, request, callback) => {
const regex = /^#?firebase(\/(.+))?/
// exclude firebase products from being bundled, so they will be loaded using require() at runtime.
if (regex.test(request)) {
return callback(null, `commonjs ${request}`) // <- use commonjs!
}
callback()
}),
})
}
}
Solved this problem!!
I'm using "gatsby": "^3.10.2", "firebase": "9.0.0-beta.6".
firebase needs to be set externals as commonjs.
gatsby-node.js:
exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
externals: getConfig().externals.concat((context, request, callback) => {
const regex = /^#?firebase(\/(.+))?/
// exclude firebase products from being bundled, so they will be loaded using require() at runtime.
if (regex.test(request)) {
return callback(null, `commonjs ${request}`) // <- use commonjs!
}
callback()
}),
})
}
}
Please try this setting.

Resources