nextjs localStorage getItem - next.js

after searching half a day I still not able to getItem from local storage.
the idea is to save some data to local storage and based on that I want to route a user in the Layout component. I am able to save to local storage and delete but not able to get data from it. I get error 'local storage not defined' or 'destroy is not a function'
I have 3 components save, delete and get. save and delete I execute after a client side api call, the get function I need to be working in the Layout as it is the top level for all routes.
I Need a bit help to the right direction please.
---Upadte
I found something that works
export const IsAuth = ()=>{
const [auth, setAuth] = useState();
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
},[]);
return auth;
}
now my problem is I have not much understanding of nextjs. I used the Layout to create a theme template, I basically have only 3 pages that can be visited if not logged in and the rest one needs to be logged in. I get so many examples but it seems like I need to verify auth on every single page instead of being able to do this on root/layout level.
all examples I get are without the use of Layout and I am totally stuck.
I want a simple login system just with jwt and check if thats there to show pages.

I could not get the localStorage.getItem() to work in the layout template.
My solution while maybe not perfect is.
in the _app.js I create useState() and pass those along to the menu trough the Layout, in in the menu useEffect() with 'use client' in the useEffect I set the state I need global.
_app.js
export default function App({ Component, pageProps }){
const [isAuth, setAuth] = useState()
const [user, setUser] = useState()
return (
<Layout setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}>
<Component user={user} setUser={setUser} isAuth={isAuth} {...pageProps} />
</Layout>
)
}
Layout.js
export default function Layout({ children, setAuth, isAuth, user, setUser }) {
return (
<>
<Headd />
<SideMenu setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}/>
<main>
<div className="menu-spacer"></div>
<content>
{children}
</content>
</main>
</>
)
}
menu.js
'use client';
const SideMenu = ({setAuth, isAuth, user, setUser}) => {
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
if(item) setUser(JSON.parse(localStorage.getItem('Ud')))
}, [router, router.isReady])
}
Now I can use the {isAuth, user,} on any page and component.
I am pretty sure this is not the right solution, but I could not find any other working solution and no one here yet posted a answer.

Related

Unable to combine a nested layout with dynamic routing

This is what I am trying to accomplish:
The main navigation is fine - I'm using a global layout component in _app.js , i.e.
const Layout = ({ Component, pageProps }: any) => {
const getLayout = Component.getLayout || ((page: any) => page);
return getLayout(<Component {...pageProps} />);
};
export default function App({ Component, pageProps }: AppProps) {
return (
<MainLayout>
<Layout Component={Component} pageProps={pageProps} />
</MainLayout>
);
}
The issue I'm having is trying to work out how to implement the side navigation - the items here will come from an API response and therefore only one request should be made. As the items are dynamic then the page needs to make use of dynamic routing.
I'm really unsure how to structure this though - I presume I just need one page i.e. blah-3/[slug].tsx. It looks like I cannot make use of the layout pattern here as there doesn't seem to be a way to pass props data to the layout.
To be honest I'm really surprised something this straightforward and such a common use case is so difficult in Next.js and would really appreciate a bit of guidance.
I have checked out what seems to be the go to article for issues related to nested routes (https://adamwathan.me/2019/10/17/persistent-layout-patterns-in-nextjs/) but this doesn't cover the dynamic routing case.

Storyblok React Bridge Restarting Preview On Data Change

I want to use the new Storyblok React bridge with Next.js. In my case, there is a component directly in the _app.js (which should not change between route changes), so it looks like this:
function MyApp({ Component, pageProps: { pageData, globalData } }) {
const story = useStoryblokState(globalData.story)
console.log('rendering app')
return (
<>
<Collage story={story} />
{/* <Component {...pageData} /> */}
</>
);
}
The corresponding getStaticProps looks like this:
export async function getStaticProps() {
let sbParams = {
version: "draft", // or 'published'
};
const storyblokApi = getStoryblokApi();
let { data: pageData } = await storyblokApi.get(`cdn/stories/home`, sbParams);
let { data: globalData } = await storyblokApi.get(
`cdn/stories/collage`,
sbParams
);
return {
props: {
pageData,
globalData,
},
revalidate: 3600,
};
}
Here everything works as intended (i.e. changes to data will only rerender the collage component but not touch anything else), but as soon as I uncomment the main component, in the Storyblok preview (with localhost:3000), changing any data will restart the iFrame, just like it would be the case if no React bridge is used. I can even see that the data changes are applied just before it restarts. Any idea how to fix this?
useStoryblokState() calls useStoryblokBridge() from the #storyblok/js package. Despite its name, useStoryblokBridge() is not a react hook, but an ordinary function with side effects. It subscribes to events from the Storyblok bridge here. If the story that was passed as argument isn't the same story that is sent to the event listener via the Storyblok bridge, the event listener will reload the entire page.
https://github.com/storyblok/storyblok-js/blob/main/lib/index.ts#L40
This means that if you call useStoryblokBridge() with two different stories, the window will reload.

Next.js withPageAuthRequired with getStaticProps

According documentation #auth0/nextjs-auth0 we can use withPageAuthRequired for trigger login screen on pages required login.
short variant: export const getServerSideProps = withPageAuthRequired();
But what to do if I need to use getStaticProps for pre-render page at build time which can't be used together with getServerSideProps? Is there any way to use withPageAuthRequired on request static generated pages?
Right now I am using double check on client side for check auth. But I would rather use a server side check as i use on other pages.
P.S. There is way to use withPageAuthRequired on client side as well. This is not suitable for my use
Since getStaticProps() is used to build a static page (i.e., no server-side logic/rendering at request time), the auth check and redirect to login will have to happen on the client side.
You might be able to get the behaviour you want by sticking a proxy in front of the static resource (e.g., using Lambda#Edge), though I'm not very familiar with this approach yet.
From your question it sounds like you are already familiar with how to do the check/redirect on the client side, but for the benefit of others who come across this post in the future:
To fetch user information on the client side, add a <UserProvider> to your app, and call the useUser() hook in client-side components.
See docs:
Wrap your pages/_app.js component with the UserProvider component:
// pages/_app.js
import React from 'react';
import { UserProvider } from '#auth0/nextjs-auth0';
export default function App({ Component, pageProps }) {
return (
<UserProvider>
<Component {...pageProps} />
</UserProvider>
);
}
You can now determine if a user is authenticated by checking that the
user object returned by the useUser() hook is defined. You can
also log in or log out your users from the frontend layer of your
Next.js application by redirecting them to the appropriate
automatically-generated route:
// pages/index.js
import { useUser } from '#auth0/nextjs-auth0';
export default function Index() {
const { user, error, isLoading } = useUser();
if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;
if (user) {
return (
<div>
Welcome {user.name}!
Logout
</div>
);
}
return Login;
}
For other comprehensive examples, see the EXAMPLES.md
document.
An alternative approach that uses withPageAuthRequired() on the client side:
import React from 'react';
import { withPageAuthRequired } from '#auth0/nextjs-auth0';
import Layout from '../components/layout';
export default withPageAuthRequired(function Profile({ user }) {
return (
<Layout>
<h1>Profile</h1>
<h4>Profile</h4>
<pre data-testid="profile">{JSON.stringify(user, null, 2)}</pre>
</Layout>
);
});
Linked from additional examples.

Reading and displaying data from a Firestore Document in React Native

Background
I'm trying to build an app which shows a number of stores, in the home screen which is a function component (mind this as I need to use hooks) I have a scroll view which shows different stores.
What I need
When the user presses on one of the stores it should redirect it to a screen which has the information of that specific store. I have built the "store detail" screen but with static info, I want to replace all of that information with data stored in a firestore collection.
Question
How would one go about retrieving data from a Firestore collection in react native, then assigning the data from each document to a separate Touchable Opacity (I know about passing params with react navigation, I just don't know which param to pass when working with Firestore), and then displaying that data in the store detail screen?
Sample code for context
App.js
<NavigationContainer>
<Stack.Navigator initialRouteName={user ? 'Home' : 'Login'}
screenOptions={{cardStyle: { backgroundColor: '#FFFFFF' }}}>
<Stack.Screen name="Home"options={{headerShown: false}}>
{props => <HomeScreen {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name="Login" component={LoginScreen} options={{headerShown: false}}/>
<Stack.Screen name="Registration" component={RegistrationScreen} options={{headerShown: false}}/>
<Stack.Screen name="storeDetail" options={{title: ''}}>
{props => <storeDetail {...props} extraData={} />}
</Stack.Screen>
</Stack.Navigator>
</NavigationContainer>
In this file you'll see that I've already called some data (Login and Register pass userData to the Home Screen), however in order to implement that method I depended on the response from the authentication method I was using. I imagine although, I will probably need to pass something as extraData, I understand what I should do, I just don't know how to fill the blank spaces.
Thanks a lot in advance!
First, install the Firebase SDK in your app, so you can make queries to your backend.
I don't know if your sample App.js represents the current state of progress on your app, but I'm going to assume that:
you already have your storeDetail screen built
you know the store's id before navigating to the screen (eg in the HomeScreen)
you pass the storeId as a navigation param when navigating to storeDetail
So in storeSetails screen, you can query Firestore when receiving storeId, and save the result to a state variable on success:
const StoreDetailsScreen = ({ route }) => { // route is passed as a prop by React Navigation
const { storeId } = route.params
const [store, setStore] = useState()
const [loading, setLoading] = useState(true) // show a loading spinner instead of store data until it's available
useEffect(() => {
const fetchQuery = async () => {
const storeData = await firestore()
.collection('stores')
.doc(storeId)
.get() // this queries the database
if (storeData) {
setStore(storeData) // save the data to store state
setLoading(false) // set loading to false
} else {
// something went wrong, show an error message or something
}
}
fetchQuery()
}, [storeId])
if (loading) {
return (
<ActivityIndicator/>
)
}
return (
// ... store details
)
}
Then you can use the data in store to render stuff in your screen
<Text>{store.name}</Text>
<Text>{store.email}</Text>
// ...
More info about how to use Firestore in RN: https://rnfirebase.io/firestore/usage

How to Manage a Navigation Menu in NextJS with WordPress

I'm building a NextJS app using headless WordPress with GraphQL. It's not clear from the documentation where I should be calling the query to create the site navigation.
https://github.com/lfades/next.js/tree/examples/cms-wordpress/examples/cms-wordpress
The navigation is controlled dynamically by WordPress Menus (Appearance > Menus) on the backend and I can successfully access these menuItems via GraphQL without any issue on the index.js and posts/[slug].js page templates in Next JS.
// index.js
export default function Index({ primaryMenu = [] }) {
return (
<Layout>
<Header>
{primaryMenu.map((item, index) => {
return (<a href={item.url}>{item.label}</a>)
)}
</Header>
</Layout>
);
}
export async function getStaticProps() {
const primaryMenu = await getPrimaryMenu(); // Get menu via GraphQL
return {
props: { primaryMenu },
};
}
The issue I'm having with this is I am repeating the getStaticProps function on each template and I should be able to use some sort of global query for this, either in the <header/> component itself or another method. I'm unable to find documentation on how to do this and it doesn't work in components.
Any guidance (or examples) on where a global query such as a dynamic Navigation query would live in a NextJS app is appreciated.
There are a couple of ways you can do it:
You can menuItems query with useQuery() from #apollo/client inside the Layout component so that its available to all pages which are wrapped inside the Layout. However the problem with this is that, there will be a load time and the data won't be prefetched and readily available like we can do with getServerSideProps() ( at page level ). Because this will be at component level.
import { useQuery } from "#apollo/client";
export default function Layout () {
const { loading, data } = useQuery( GET_MENU_QUERY )
return {...}
}
You can use swr that uses caching strategy. There is blog that explains how to use it
I battled this for a while (for JD site) with redux and wp rest, but I think theory should be the same for gql + apollo client.
You need to override Next App _app with a custom class that extends App.
And you might need to inject an instance of apollo client into AppContext using a HOC. I used this wrapper for Redux. Would need to be modelled after that.
Edit: (Looks like someone has made it already)
// export default withRedux(makeStore)(MyApp);
export default withApollo(apolloClient)(MyApp); ???
Then in your App getInitialProps, you can make query to get menu. By default apollo client query will grab cached value if it's in the cache store already I believe.
static async getInitialProps(appContext) {
const { isServer, pathname, apollo? } = appContext.ctx;
// do menu query
const menu = apollo.query???
// Redux version
// const state = store.getState();
// let main_menu = state.menu;
// if (!state.menu) {
// const menu = await apiService().getMenu("main");
// main_menu = menu;
// store.dispatch({ type: "SET_MENU", payload: menu });
// }
...
// call the page's `getInitialProps` and fills `appProps.pageProps`
const initialProps = await App.getInitialProps(appContext);
const appProps: any = {
...initialProps,
menu: main_menu
};
return appProps;
}
Now menu is in the page props of the App Component, which can be passed down.
Or you can use apollo client to make the query again in a child component. So when you make the query again, in header or whatever, it will take the cached response provided it's the same query.
I made an endpoint for menus that included the template name + post slug along with the menu items and mapped the wp templates to next routes.
const menu = useSelector((state: any) => state.menu);
const menuItems = menu.map((item: any) => {
const path = getTemplatePath(item.template);
return (
<Link key={item.slug} href={`/${path}`} as={`/${item.slug}`} scroll={false}>
<a>{item.title}</a>
</Link>
);
});

Resources