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.
Related
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>
)}
I'm using getInitialProps on _app to grab cookies and pass the values down to a component to set an element's width, but it's always sending undefined.
Here is the _app page where I'll be fetching the cookies
// _app.tsx -> page
function MyApp({ Component, pageProps }: AppProps) {
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
MyApp.getInitialProps = async (appContext) => {
const pageProps = App.getInitialProps(appContext);
const { req } = appContext.ctx;
const cookies = parseCookies(req);
return {
...pageProps,
initialAsideLeftHandler: cookies.asideLeftHandler
};
}
export default MyApp;
Pass it down to layout component for further use in asideLeft component
// Layout.tsx -> component
interface Props {
initialAsideLeftHandler: {
x: number,
y: number,
width:number,
height:number
},
children: JSX.Element[] | JSX.Element
}
const Layout = (props: Props) => {
const { children } = props;
const [isAsideRightOpen, setIsAsideRightOpen] = useState(true);
return (
<>
<AsideLeft initialAsideLeftHandler={props.initialAsideLeftHandler}>
</AsideLeft>
/* everything else */
<>
)
}
Component where I'll be using the fetched cookies from server but unfortunately as I parse it, it returns an error: Unexpected token u in JSON at position 0
// asideLeftHandler.tsx -> component
function AsideLeft({initialAsideLeftHandler}) {
const router = useRouter();
const state = JSON.parse(initialAsideLeftHandler)
const [asideLeftHandler, setAsideLeftHandler] = useState(JSON.parse(initialAsideLeftHandler)); // ---> line of error: Unexpected token u in JSON at position 0
useEffect(() => {
Cookie.set("asideLeftHandler", JSON.stringify(asideLeftHandler), { expires: 7});
}, [asideLeftHandler]);
return (
<Resizeable
width={asideLeftHandler.width}
>
/* everything else */
)
}
Your _app should be like this
function MyApp({ Component, pageProps, initialAsideLeftHandler }: AppProps) {
return (
<Layout initialAsideLeftHandler={initialAsideLeftHandler}>
<Component {...pageProps} />
</Layout>
);
}
MyApp.getInitialProps = async (appContext) => {
const pageProps = App.getInitialProps(appContext);
const { req } = appContext.ctx;
const cookies = parseCookies(req);
return {
...pageProps,
initialAsideLeftHandler: cookies.asideLeftHandler
};
}
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);
};
I have an AuthContext to manage the authentication in my Next.js app. The _app.js file looks like this:
import '../styles/global.css'
import 'tailwindcss/tailwind.css'
import { AuthProvider } from '../components/AuthContext'
function MyApp({ Component, pageProps }) {
return (
<>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</>
)
}
export default MyApp
And the AuthContext file is something like this:
import React, { useContext, useState, useEffect } from "react"
import { auth, db } from "./Firebase";
import { doc, getDoc } from "firebase/firestore";
const AuthContext = React.createContext()
export function AuthProvider({ children }) {
const [currentUser, setCurrentUser] = useState([])
const [loading, setLoading] = useState(true)
const [userData, setUserData] = useState({})
const [companyData, setCompanyData] = useState({})
useEffect(() => {
const unsubscribe =
auth.onAuthStateChanged(user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
useEffect(() => {
const fetchData = async () => {
const qu = doc(db, "users", currentUser.uid)
const datau = await getDoc(qu)
setUserData(datau.data());
const qc = doc(db, "companies", currentUser.uid)
const datac = await getDoc(qc)
setCompanyData(datac.data());
}
currentUser?.uid && fetchData()
}, [currentUser])
const value = {
currentUser,
userData,
companyData
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
export function useAuth() {
return useContext(AuthContext)
}
Then I have a dynamic page [id].js that is something like this:
import { useAuth } from '../../components/AuthContext';
import Head from 'next/head';
export async function getStaticProps(context) {
// fetch and return data
}
export async function getStaticPaths() {
// fetch paths
return { paths, fallback: 'blocking' }
}
export default function Job(props) {
const { userData, currentUser } = useAuth();
const data = props.data;
return (
<>
<Head>
{data.title} | SiteName
</Head>
<h1>data.title</h1>
{currentUser ? userData.name : null}
</>
)
}
Problem: The website works perfectly, BUT when I check the page [id].js source code, Next.js doesn't build the HTML structure with head and body optimized for SEO (which is the main reason why I'm migrating to Next.js).
If I remove the "!loading &&" (see below) in the AuthContext file, the Next's SSG HTML generation works BUT the whole app starts to give me errors everywhere.
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
What exactly is the problem? And any idea on how to solve it?
I have a custom _app.js which looks like this:
function MyApp({ Component, pageProps }) {
const Layout = Component.layoutProps?.Layout || React.Fragment
const layoutProps = Component.layoutProps?.Layout
? { layoutProps: Component.layoutProps }
: {}
const meta = Component.layoutProps?.meta || {}
const description =
meta.metaDescription || meta.description || 'Meta Description'
const store = useStore(pageProps.initialReduxState)
return (
<QueryClientProvider client={queryClient}>
<Provider session={pageProps.session}>
<Title suffix="My Dynamic Site">{meta.metaTitle || meta.title}</Title>
<Description>{description}</Description>
<Meta />
<ReduxProvider store={store}>
<PersistGate persistor={store.__PERSISTOR} loading={null}>
<CartBox />
<Layout {...layoutProps}>
<Component {...pageProps} />
</Layout>
</PersistGate>
</ReduxProvider>
<ReactQueryDevtools initialIsOpen={false} />
</Provider>
</QueryClientProvider>
)
}
MyApp.getInitialProps = async (appContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps }
}
export default MyApp
Now, I would like to fetch data using ssg/ssr data fetching method to help SEO team for my page components.
But, it seems any of the methods aren't working as expected, none of them actually passing props to the page component.
Here's my page component.
const HomePage = ({ title, stars }) => {
console.log(title, stars); // undefined, undefined
return (
<div>
<Header title={title} />
<GhStars stars={stars} />
<Footer />
</div>
)
}
export const getStaticProps = async () => {
return {
props: {
title: "My Dynamic Title From getStaticProps"
}
}
}
// I tried both getInitialProps & getStaticProps independently.
HomePage.getInitialProps = async (ctx) => {
const res = await fetch('https://api.github.com/repos/vercel/next.js')
const json = await res.json()
return { stars: json.stargazers_count }
}
export default HomePage
I might be missing something for sure, which I failed to figure out so far.
Any help will be really much appreciated. Thanks.