It seems nextjs getStaticProps is not working in components but working in pages. But I need to fetch api data to my components. Is there anyway to this?
I've tried this but I'm not happy with the process .
const [data, setData]=useState();
useEffect(()=>{
async function fetchData() {
const res = await fetch(
'https://domainname/api/vb1/category-tree'
);
const {data} = await res.json();
setData(data)
}
fetchData()
},[]);
If I understand correctly, you want to statically generate certain components using getStaticProps. In my opinion, the easiest way to do this would be to fetch the data you need in the page where your component is being used and pass the data as props to the component.
Related
I have a react application when i have three components that each of them uses useAxios.js hook to make an api call to fetch data (three different endpoints) and then returns data, error, loading states, now i want to make a loading screen while the app is loading, i want to show the loading screen while the components fetch data from the server. i am not sure how to keep track of the three calls when each of them have separate loading states.
how to implement a loading screen for that application?
i am not sure if i can use useContext to make a global state or using useReducer
If you app got simple state:
Just lift up your loading state to parent component and pass to children as props.
If you got complex state in your app:
Option1 - You can create some react context for handle loading state and use useContext in any child component.
Option2 - Use state management such as redux or react-query for handle your loading state.
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
axios.get(url)
.then((res) => {
setData(res.data)
setLoading(false)
}).catch(err => {
setError(err)
setLoading(false)
})
}, [url]);
return { data, loading, error }
};
export default useFetch;
You can use this custom hook in your components like this for dynamic use
const { data, loading, errors } = useFetch('localhost:3000/api/user')
In this way you can easily track all the request separately and can implement your loader accodingly
loading ? <Loader /> : <Random />
I'm trying to use the same query with different variables using useLazyQuery. I built a hook for this reason and it fetches everything alright. However, it never really uses cached data. I looked into the cache and it's only storing the response from the last request in the array. This is a simplified version of my hook:
const useProductSearchQuery = (categories) => {
const [getProducts] = useLazyQuery(QUERY);
const [data, setData] = useState();
useEffect(() => {
async function getProducts() {
const responses = await Promise.all(
categories.children.map((cat) =>
getProducts({ variables: { category: cat.id } })
)
);
setData(responses);
}
getProducts();
}, [productCategories, getProducts]);
return { data };
};
I'm not sure if this use case fits useLazyQuery so maybe that's why it doesn't work. I just needed an imperative way of running my queries and this seemed easier than using a consumer to pass the client around.
The other alternative would be to just iterate categories and then have a useQuery hook for each but I'd prefer having my data ready in one go.
Nevermind I didn't notice the hooks docs mention the useApolloClient hook (https://www.apollographql.com/docs/react/api/react/hooks/#useapolloclient)... Works like a charm!
I have multiple getServerSideProps in my project and I have a header which displays pages and I have to wait for a page to be opened once I click upon it since I need data to be fetched. Once they are fetched the page will be open.
One approach I used to show user a loading state is to use routeChangeStart BUT I stumbled upon one problem and so I would like not to use this case.
If I go on a page and the data is fetching I want to show user a spinner or some indicator and once the data is fetched I want to stop the indicator/spinner.
As you probably figured out, getServerSideProps runs on the server and is blocking. The fetch request needs to complete before the HTML is sent to the user (i.e., the page is changed). So if you want to show a loading indicator, you need to move that fetch request to the client.
For instance, if you probably have a page with this basic structure:
export default function Page({ data }) {
return <div>{data.name}</div>
}
export async function getServerSideProps() {
const response = await fetch('https://example.com/api')
const data = await response.json()
return {
props: { data },
}
}
const fetcher = url => fetch(url).then(res => res.json());
export default function Page() {
const { data } = useSWR('https://example.com/api', fetcher)
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
Or if you don't need SWR and can use a simple fetch request:
export default function Page() {
const [data, setData] = useState()
useEffect(() => {
fetch('https://example.com/api')
.then(async(response) => {
const json = await response.json()
setData(json)
})
})
if (!data) return <LoadingSpinner />
return <div>{data.name}</div>
}
P.S. If the initial fetch request in getServerSideProps used sensitive information (e.g., API secret credentials), then go ahead and setup a Next.js API route to handle the sensitive part and then fetch the new route.
I just used routeChangeStart.
I didn't want to use it since router.push('/map') didn't work in pages/index.tsx file but I solved this issue by creating a new component putting router.push in useeffect and rendering a loader.
routeChangeStart was in _app.js and because of this in index.js router.push() didn't work - I tested it
routeChangeStart - how it works?
When we click on a page the data is being fetched on the server and the page will only be displayed to us once the data is fetched. So we can make the next thing, we can just intercept the route change.
When we click on a link(we wait for data to fetch) we set loading state in routeChangeStart to true and if we moved to another page(it means we fetched the data) we invoke routeChangeComplete which runs once we moved to the route we wanted to, and here we set loading state to false. And after this I just pass the loading state using React Context
I was wondering what the best practice is for storing/using data from an API in a react app. Here's a hypothetical example:
A simple app that fetches blog posts from an API and a control bar above the posts that can filter the posts by date, category, etc.
Which approach would be better:
1). Fetch every single post with useEffect, for example, and then store the returned data in state, and then create the filtering functions which filter the data that is stored in state.
2). Fetch every single post on first render, again with useEffect, and then create filtering functions which make multiple requests to the API for the required data.
Something like this
const BlogPage = () => {
//state where store posts (if u don't use redux)
const [data, setData] = useState([])
//request with or without params
const fetchData = async (queryParams = '') => {
// res = await request....
setData(res)
}
useEffect(() => {
fetchData()
}, [])
return <>
<Filters onSubmit={fetchData}/>
<Table data={data}/>
</>
}
You can update this with loaders, empty data render templates, adding redux or other
If you don't have an API with query params and you have no server pagination you must write filter function and replace <Filters onSubmit={fetchData}/> to <Filters onSubmit={filter}/>. The result of this function must call setData.
I am using NextJS's getStaticProps to fetch some data from an external API. Reading the data fetching documentation on getStaticProps I came across this particular note:
Note: You should not use fetch() to call an API route in your
application. Instead, directly import the API route and call its
function yourself. You may need to slightly refactor your code for
this approach.
Right now I am calling getStaticProps directly from a page component called Index as follows:
export default function Index({ data }) {
return <div>{data}</div>;
}
export async function getStaticProps() {
const response = await fetch("http://127.0.0.1:8000/data");
const data = await response.json();
return { props: { data } };
}
As per the above documentation, this should not be done. How can I restructure my code to fetch data correctly? What does it mean to "import the API route and call its function yourself?"
I think that the Note is related to internal api path
You should not use fetch() to call an API route in your application
I suppose is related to every path that you define in /pages/api/*. Instead of fetch you can simply refactor your code and import data.
So, the code below is correct, don't need any refactoring
export default function Index({ data }) {
return <div>{data}</div>;
}
export async function getStaticProps() {
const response = await fetch("http://127.0.0.1:8000/data");
const data = await response.json();
return { props: { data } };
}