React Query with firebase returns undefined - next.js

I am very new to state management libraries and the one that I chose was React Query. I have followed multiple tutorials about it with mock data, but when I try to use my firebase imported data, it returns undefined. I am attaching all of my code with instances of React Query in it. This app is in next js 12.
file where I want to fetch and render data
const fetchDbData = async () => {
const {currentUser} = UseAuth();
function getLoc() {
if (currentUser) {
return 'users/' + currentUser.uid + '/userInvestments'
} else {
return 'users/TestDocumentForDatabase/userInvestments'
};
}
const loc = getLoc();
const q = query(collection(db, loc));
const snapshot = await getDocs(q);
console.log(snapshot)
const {data, status, error} = useQuery(
['firebaseData'],
() => snapshot.forEach(doc => doc.data()),
{
refetchOnWindowFocus: false
},
{
retry: false
},
)
return {data, status, error}
}
export default function usePortfolio() {
const {data, status, error} = fetchDbData
.....
_app.js
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
useInfiniteQury: false,
},
}
})
export default function App({ Component, pageProps }) {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider >
<RootLayout>
<Component {...pageProps} />
</RootLayout>
</AuthProvider>
</QueryClientProvider>
)}

Related

Next.js reference error "client" is not defined

I am having an error when trying to fetch product data from sanity with getStaticPaths
here is my code:
import React, { useState } from "react";
function ProductPage({ product, products }) {
const { image, name, details, price } = product;
return (
<div className="pdPage">
<div className="container">{name}</div>
</div>
);
}
export default ProductPage;
export const getStaticPaths = async () => {
const query = `*[_type == "product"] {
slug {
current
}
}
`;
const products = await client.fetch(query);
const paths = products.map((product) => ({
params: {
slug: product.slug.current,
},
}));
return {
paths,
fallback: "blocking",
};
};
export const getStaticProps = async ({ params: { slug } }) => {
const query = `*[_type == "product" && slug.current == '${slug}'][0]`;
const productsQuery = '*[_type == "product"]';
const product = await client.fetch(query);
const products = await client.fetch(productsQuery);
console.log(product);
return {
props: { products, product },
};
};
And then I get reference error client is not defined from getstatic client.fetch
I even delete my code and replace from tutor github repository, but get the same error
After figure it out sometimes I found that I forgot to import client from

next-redux-wrapper HYDRATION failed

I am trying to integrate next-redux-wrapper to Next.js RTK project. When invoking async action from getServerSideProps, I am getting state mismatch error (see the image below).
When I dispatch action from client side (increment/decrement), everything works well. I think issue is related to HYDRATION but so far all my efforts have failed.
I tried mapping redux state to props, storing props in component state, added if statements to check values but nothing seem to work. I've been stuck on this for 2 weeks. I'm not sure what else to try next.
"next": "12.3.1",
"next-redux-wrapper": "^8.0.0",
"react":
"18.2.0",
"react-redux": "^8.0.4"
store/store.js
import { configureStore, combineReducers } from "#reduxjs/toolkit";
import counterReducer from "./slices/counterSlice";
import { createWrapper, HYDRATE } from "next-redux-wrapper";
const combinedReducer = combineReducers({
counter: counterReducer,
});
const reducer = (state, action) => {
if (action.type === HYDRATE) {
const nextState = {
...state, // use previous state
...action.payload, // apply delta from hydration
};
return nextState;
} else {
return combinedReducer(state, action);
}
};
export const makeStore = () =>
configureStore({
reducer,
});
export const wrapper = createWrapper(makeStore, { debug: true });
store/slices/counterSlice.js
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
const initialState = {
value: 0,
data: { quote: "" },
pending: false,
error: false,
};
export const getKanyeQuote = createAsyncThunk(
"counter/kanyeQuote",
async () => {
const respons = await axios.get("https://api.kanye.rest/");
return respons.data;
}
);
export const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(getKanyeQuote.pending, (state) => {
state.pending = true;
})
.addCase(getKanyeQuote.fulfilled, (state, { payload }) => {
state.pending = false;
state.data = payload;
})
.addCase(getKanyeQuote.rejected, (state) => {
state.pending = false;
state.error = true;
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
pages/index.js
import React, { useState } from "react";
import { useSelector, useDispatch, connect } from "react-redux";
import {
decrement,
increment,
getKanyeQuote,
} from "../store/slices/counterSlice";
import { wrapper } from "../store/store";
function Home({ data }) {
const count = useSelector((state) => state.counter.value);
// const { data, pending, error } = useSelector((state) => state.counter);
const dispatch = useDispatch();
const [quote, setQuote] = useState(data.quote);
return (
<div style={{ display: "flex", flexDirection: "column" }}>
{/* <span>{pending && <p>Loading...</p>}</span>
<span>{error && <p>Oops, something went wrong</p>}</span> */}
<div>{quote}</div>
<span>Count: {count}</span>
<div>
<button
aria-label="Increment value"
onClick={() => dispatch(increment())}
>
Increment
</button>
<button
aria-label="Decrement value"
onClick={() => dispatch(decrement())}
>
Decrement
</button>
</div>
</div>
);
}
export const getServerSideProps = wrapper.getServerSideProps(
(store) =>
async ({ req, res, ...etc }) => {
console.log(
"2. Page.getServerSideProps uses the store to dispatch things"
);
await store.dispatch(getKanyeQuote());
}
);
function mapStateToProps(state) {
return {
data: state.counter.data,
};
}
export default connect(mapStateToProps)(Home);
Errors in console
This might stem from a known issue where next-redux-wrapper 8 hydrates too late. Please try downgrading to version 7 for now and see if that resolves the problem.

How to add Spinner at specific route?

I get this code in question:nextjs getServerSideProps show loading
import Router from "next/router";
export default function App({ Component, pageProps }) {
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
const start = () => {
console.log("start");
setLoading(true);
};
const end = () => {
console.log("findished");
setLoading(false);
};
Router.events.on("routeChangeStart", start);
Router.events.on("routeChangeComplete", end);
Router.events.on("routeChangeError", end);
return () => {
Router.events.off("routeChangeStart", start);
Router.events.off("routeChangeComplete", end);
Router.events.off("routeChangeError", end);
};
}, []);
return (
<>
{loading ? (
<h1>Loading...</h1>
) : (
<Component {...pageProps} />
)}
</>
);
}
It work for me, but with all route, I just want to work with some route, and route have dynamic query, How can I do it?
First of all, you need to define a list of routes that you don't want to have a loading state. For example
//`/details/` can be a dynamic route like `/details/1`, `/details/2`, etc.
const EXCEPTED_ROUTES = ['/details/'] //routes based on definitions
And then you can do it with URL param in routeChangeStart event
const start = (url) => {
const isURLMatched = EXCEPTED_ROUTES.some(exceptedRoute => url.startsWith(exceptedRoute))
if(isURLMatched) { //the condition here is your choice
return //should not set loading state
}
console.log("start");
setLoading(true);
};

React-query with NextJs routing

I have a page with few queries and it's all working until I update the URL params and then react-query stops working and also disappear from dev-tools.
When I click on a row it triggers the handleRowClick function which update the URL with params, and then react query stops working.
1.First Image
2.Clicking on a row ( Update URL params ) Second Image
const Volumes: NextPage = () => {
const apiRef = useGridApiRef()
const [volumes, setVolumes] = useState<IDataset[] | undefined>(undefined)
const [isOpen, setOpen] = useState(false)
const [rightWingData, setRightWingData] = useState<IDataset | undefined>(undefined)
const [searchValue, setSearch] = useState('')
const [volumeId, setVolumeId] = useState('')
const { isLoading, data } = useVolumeData()
const { isLoading: searchIsLoading, data: searchData } = useSearchVolumes(searchValue)
const { isLoading: volumeByIdLoading, data: volumeByIdData } = useVolumeDataById(volumeId)
const router = useRouter()
useEffect(() => {
if(router.isReady && router.query?.id && !rightWingData){
const volumeId = router.query.id.toString()
setVolumeId(volumeId)
}
if (!isLoading && data && !searchData) {
setVolumes(data.data.result)
}
else if (!searchIsLoading) {
setVolumes(searchData)
}
if(!volumeByIdLoading && volumeByIdData){
showVolumeData(volumeByIdData)
}
}, [data, isLoading, searchIsLoading, searchData, isOpen, rightWingData, volumeByIdLoading, volumeByIdData])
const handleRowClick = (rowData: IRowData) => {
showVolumeData(rowData.row)
Router.push({
pathname: '/volumes',
query: { id: rowData.row.id},
})
}
const showVolumeData = (volumeData: IDataset) => {
apiRef.current.updateColumns(thinColumns)
setRightWingData(volumeData)
setOpen(true)
setVolumeId('')
}
const closeRightWing = () => {
setOpen(false)
Router.push('/volumes')
apiRef.current.updateColumns(columns)
}
if (isLoading || !volumes) return <h1>Is Loading...</h1>
return (
<div className={`volumes ${isOpen ? "open" : "close"}`}>
<div className="volumes-table">
<InfTable setSearch={setSearch} searchValue={searchValue} apiRef={apiRef}
rows={volumes} columns={columns} initialState={initialState} onRowClick={handleRowClick} />
</div>
{rightWingData &&
<div className="right-wing-wrapper" >
<VolumeRightWing volume={rightWingData} onClose={closeRightWing} />
</div>
}
</div>
)
}
function MyApp({ Component, pageProps }: AppProps) {
const queryClient = new QueryClient()
return (
<QueryClientProvider client={queryClient}>
<Layout>
<Component {...pageProps} />
<ReactQueryDevtools initialIsOpen={false} position="bottom-right" />
</Layout>
</QueryClientProvider>
)
}
as shown in the code, you are re-creating a new QueryClient inside the App component:
function MyApp({ Component, pageProps }: AppProps) {
const queryClient = new QueryClient()
which means that every time the App re-renders, you throw away the Query Cache (which is stored inside the client).
This is likely what happens when you change the route params. If you cannot create the client outside of MyApp (e.g. because you are using server-side rendering), it is advised to create a stable client:
function MyApp({ Component, pageProps }: AppProps) {
const [queryClient] = React.useState(() => new QueryClient())
this is also shown in the SSR docs.

Dynamic Content not loading in NextJs Dynamic Pages

Currently I have a Blogs collection type in my Strapi CMS with an Id and title data fields. I'm using NextJs for my frontend to dynamically load blog content for each blog page. But my content doesn't load when my dynamic page is loaded.
Page where individual blogs are stored:
{posts &&
posts.map((item, idx) => (
<Link href={`/BlogPage/${item.id}`}>
<div>
<img src={`http://localhost:1337${item.Thumbnail.url}`}/>
</div>
</Link>
Then inside my BlogPage directory i have a file [id].js:
export default function name({blog}) {
return (
<>
<div>
{blog.Title}
</div>
</>
)}
// Tell nextjs how many pages are there
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/blogs");
const posts = await res.json();
const paths = posts.map((blog) => ({
params: { id: blog.id.toString() },
}));
return {
paths,
fallback: false,
};
}
// Get data for each individual page
export async function getStaticProps({ params }) {
const { id } = params;
const res = await fetch(`http://localhost:1337/blogs?id=${id}`);
const data = await res.json();
const posts = data[0];
return {
props: { posts },
};
}
This takes me to this URL http://localhost:3000/BlogPage/1 and gives me an error
TypeError: Cannot read property 'Title' of undefined
Try to out the getStaticProps and getStaticPaths of name function
export async function getStaticPaths() {
const res = await fetch("http://localhost:1337/blogs");
const posts = await res.json();
const paths = posts.map((blog) => ({
params: { id: blog.id.toString() },
}));
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const { id } = params;
const res = await fetch(`http://localhost:1337/blogs?id=${id}`);
const data = await res.json();
const posts = data[0];
return {
props: { posts },
};
}
export default function name({posts }) { // change this line
return (
<>
<div>
{posts.Title} // change this line // Are you sure is it Title? not title? if it is with lowercase, it will return null
</div>
</>
)
}

Resources