I'm using i18n in my next app, I need to access the current page language in getStaticProps then i fetch data
export const getStaticProps = async () => {
//need to get language here
return {
props: { data },
};
};
const App = ({ data }) => {
//my component where i can get language
const { t, i18n } = useTranslation();
const currentLang = i18n.language;
};
You can get the local from the object that is passed to the getStaticProps.
You can see it in this example from Vercel
In you case it could look like this:
export const getStaticProps = async ({ locale }) => {
doSomethingWithLocale(locale)
return {
props: { data },
};
};
Related
I am creating a blog in Next.js and using Strapi headless CMS as backend.
I was trying to get data from the API I made from Strapi.
For fetching data I made
./client.js
export default class StrapiClient{
constructor(){}
/*API_URL = "http://localhost:1337/api/"*/
async fetchData(path){
const url = `${process.env.API_URL}${path}`
const res = await fetch(url)
const posts = await res.json()
return posts
}}
and imported it to ./components/blog.js
import StrapiClient from '../client'
const Client = new StrapiClient
export const getStaticProps = async () => {
const posts = await Client.fetchData(`articles`)
return{
props: {
posts,
}
}
};
const Blog = ({posts}) => {
return (
<div>
{posts.data.map((element) => {
return(
<div key={element.id}>
<h1 className=" text-2xl">{element.attributes.title}</h1>
</div>
)
})}
</div>
);
};
export default Blog;
but I got error
TypeError: Cannot read properties of undefined (reading 'data')
and here is the structure of data I was using
{
"data" : [
"id" /*string*/
]
}
You need to await the async function to get data from Promise
export const getStaticProps = async () => {
const posts = await Client.fetchData(`articles`)
return{
props: {
posts,
}
}
};
Async functions always return a promise
Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
const posts = Client.fetchData(`articles`)
I think you need to await for the fetchData.
Edit:
I just noticed you are using getStaticPaths function instead of getStaticProps. Can you please change the name and try again?
From next.js documentation the getStaticPaths method is used to define a list of paths to be statically generated but to fetch data for page you need to use getStaticProps:
export async function getStaticProps() {
const posts = await Client.fetchData(`articles`);
return {
props: {
posts,
},
}
}
Or fetch data using getServerSideProps without use getStaticPaths:
export async function getServerSideProps() {
const posts = await Client.fetchData(`articles`);
return { props: { posts } }
}
I want to create basic Next.js HOC for authentication. I searched but I didn't figure it out.
I have an admin page in my Next.js app. I want to fetch from http://localhost:4000/user/me and that URL returns my user. If user data returns, component must be rendered. If data didn't return, I want to redirect to the /admin/login page.
I tried this code but that didn't work. How can I solve this issue? Also can I use useSWR instead of fetch?
const withAuth = (Component, { data }) => {
if (!data) {
return {
redirect: {
destination: "/admin/login",
},
};
}
return Component;
};
withAuth.getInitialProps = async () => {
const response = await fetch("http://localhost:4000/user/me");
const data = await response.json();
return { data };
};
export default withAuth;
const AdminHome = () => {
return ();
};
export default withAuth(AdminHome);
Server-side authentication
Based on the answer from Create a HOC (higher order component) for authentication in Next.js, you can create a re-usable higher-order function for the authentication logic.
If the user data isn't present it'll redirect to the login page. Otherwise, the function will continue on to call the wrapped getServerSideProps function, and will return the merged user data with the resulting props from the page.
export function withAuth(gssp) {
return async (context) => {
const response = await fetch('http://localhost:4000/user/me');
const data = await response.json();
if (!data) {
return {
redirect: {
destination: '/admin/login'
}
};
}
const gsspData = await gssp(context); // Run `getServerSideProps` to get page-specific data
// Pass page-specific props along with user data from `withAuth` to component
return {
props: {
...gsspData.props,
data
}
};
}
}
You can then use it on the AdminHome page to wrap the getServerSideProps function.
const AdminHome = ({ data }) => {
return ();
};
export const getServerSideProps = withAuth(context => {
// Your normal `getServerSideProps` code here
return { props: {} };
});
export default AdminHome;
Client-side authentication
If you'd rather have the authentication done on the client, you can create a higher-order component that wraps the component you want to protect.
const withAuth = (Component) => {
const AuthenticatedComponent = () => {
const router = useRouter();
const [data, setData] = useState()
useEffect(() => {
const getUser = async () => {
const response = await fetch('http://localhost:4000/user/me');
const userData = await response.json();
if (!userData) {
router.push('/admin/login');
} else {
setData(userData);
}
};
getUser();
}, []);
return !!data ? <Component data={data} /> : null; // Render whatever you want while the authentication occurs
};
return AuthenticatedComponent;
};
You can then use it to wrap the AdminHome component directly.
const AdminHome = () => {
return ();
};
export default withAuth(AdminHome);
If you're looking for the typescript version:
withAuth.ts
export function withAuth(gssp: GetServerSideProps): GetServerSideProps {
return async (context) => {
const { user } = (await getSession(context.req, context.res)) || {};
if (!user) {
return {
redirect: { statusCode: 302, destination: "/" },
};
}
const gsspData = await gssp(context);
if (!("props" in gsspData)) {
throw new Error("invalid getSSP result");
}
return {
props: {
...gsspData.props,
user,
},
};
};
}
Home.tsx
export const getServerSideProps = withAuth(async (context) => {
return { props: {} };
});
I want to fetch single data by id and I am using getStaticPaths and getStaticProps but I am getting the error data is not defined. Where am I going wrong Please help
My [id].tsx file
import MainComponentLayout from "../../components/Layout/MainLayoutComponent"
import EditProject from "../../components/EditProjectForm";
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`)
const data = await response.json()
console.log(data)
const path = data.result.map(project => {
return{
params: {id:project.project_ID}
}
})
return{
paths:path,
fallback: false
}
}
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects/${id}`)
// Single Object
const data = await response.json()
return{
props: {fetchedData:data},
}
}
const EditForm = () => {
return(
<MainComponentLayout ComponentToRender = {<EditProject fetchedData = {fetchedData}/>}/>
)
}
export default EditForm
Change const EditForm = () => { to const EditForm = ({fetchedData}) => and it will work.
The getStaticProps, as its name implies, passes the fetched props object to the function as properties. You need to define them in the function as an object, and you can also destructure as in the example above, basically defining a fetchedData variable.
If You want to use props {fetchedData:data} in your app, You need pass them to the page component as props. As we can read in docs:
props - An optional object with the props that will be received by the
page component. It should be a serializable object
Here You have example page with getStaticProps() correctly used.
and Your code with props, Good Luck ! ;-)
import MainComponentLayout from "../../components/Layout/MainLayoutComponent";
import EditProject from "../../components/EditProjectForm";
const EditForm = ({ fetchedData }) => {
return (
<MainComponentLayout
ComponentToRender={<EditProject fetchedData={fetchedData} />}
/>
);
};
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id;
const response = await fetch(
`http://b560-45-248-23-129.ngrok.io/projects/${id}`
);
// Single Object
const data = await response.json();
return {
props: { fetchedData: data },
};
};
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`);
const data = await response.json();
console.log(data);
const path = data.result.map((project) => {
return {
params: { id: project.project_ID },
};
});
return {
paths: path,
fallback: false,
};
};
export default EditForm;
I have an index page that includes data fetching within getServerSideProps.
If I use next/link or maybe router.push() - Is there a way for that data to persist across to the rest of the pages in my app?
Or is this a scenario where I'd need to use something like Context/Redux?
For example:
index.tsx
const App = ({ productData }: IndexProps) => {
return (
<Link href={`/product/${title}`}> ... </Link>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
const productData = await getProducts();
return {
props: { productData },
};
};
/product/[id].tsx
const Product = ({ productData }) => {
return (
<Link href={`/product/${title}`}> ... </Link>
)
}
export const getServerSideProps: GetServerSideProps = async () => {
if (PRODUCTDATADOESNTEXIST) {
const productData = await getProducts();
}
// else use data fetched in from previous page?
return {
props: { productData },
};
};
Thanks!
You can create a service that will cache the data in memory.
Something like this.
const cache = {};
export const setProducts = (products) => {
products.forEach((p) => {
cache[p.id] = p;
});
};
const getProduct = (id) {
if(cache[id]){
Promise.resolve(cache[id]);
}
// make the API call to fetch data;
}
export default getProduct;
Use the set method to store the data from the NextJS Page and use the get method to fetch data when needed.
I want to have a way to get and fetch the current user using React Context (to not pass props to all my components)
I tried using React Context but didn't understand how I would achieve something like const { currentUser, fetchCurrentUser } = useCurrentUser() from the docs.
here is what i did for my project:
// src/CurrentUserContext.js
import React from "react"
export const CurrentUserContext = React.createContext()
export const CurrentUserProvider = ({ children }) => {
const [currentUser, setCurrentUser] = React.useState(null)
const fetchCurrentUser = async () => {
let response = await fetch("/api/users/current")
response = await response.json()
setCurrentUser(response)
}
return (
<CurrentUserContext.Provider value={{ currentUser, fetchCurrentUser }}>
{children}
</CurrentUserContext.Provider>
)
}
export const useCurrentUser = () => React.useContext(CurrentUserContext)
and then use it like this:
setting up the provider:
// ...
import { CurrentUserProvider } from "./CurrentUserContext"
// ...
const App = () => (
<CurrentUserProvider>
...
</CurrentUserProvider>
)
export default App
and using the context in components:
...
import { useCurrentUser } from "./CurrentUserContext"
const Header = () => {
const { currentUser, fetchCurrentUser } = useCurrentUser()
React.useEffect(() => fetchCurrentUser(), [])
const logout = async (e) => {
e.preventDefault()
let response = await fetchWithCsrf("/api/session", { method: "DELETE" })
fetchCurrentUser()
}
// ...
}
...
the full source code is available on github: https://github.com/dorianmarie/emojeet
and the project can be tried live at: http://emojeet.com/
If useContext returns undefined, then you might have forgotten the Provider or need to move your provider up the stack.
U dont explained what u want to do with the data but...After u exec the function fetch in use effect.
Now u have the object user in the state currentUser.
Try to console log after the use effect the currentUser and see what dat inside it.
After u can use it with currentUser."whatever prop inside"..