I don't think the getServerSideProps gets run, I'm just starting out and got no clue how to fix this, tried for a few hours but still getting undefined from the IndexPage console.log(props.data)
export default function IndexPage(props) {
console.log(props.data.copyright);
return (
<>
<div>{props.data.copyright}</div>
</>
)
}
export async function getServerSideProps() {
const res = await fetch(" https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY");
const data = await res.json();
return { props: { data } };
}
Edited: the code works perfectly on local machine and vercel deployment, but not on codesandbox.io where I originally started /headache
You have to use a React Hook, and then call the function inside of that. You can then store the response in a state instead
Try something like this:
import fetch from "isomorphic-unfetch";
import React, {useEffect} from "react"
const IndexPage = props => {
console.log(props.data);
if(!props.data){
return <div>Waiting for data...</div>
}
return <div>main page</div>;
};
export async function getServerSideProps() {
const { API_URL } = process.env;
console.log("inside fetch");
const res = await fetch(`${API_URL}/movies`);
const data = await res.json();
return { props: { data } };
}
export default IndexPage;
Otherwise you can use the
getStaticPropsThen ou are ensured, that the data is there, when the component fetches..
Related
I have a scenario where i want to first make a request and validate the response from the server and then show UI. Complete Response also needs to be passed in the component. I have sample code and i got the proper response from server but it does not get passed to the component. I am using props to pass data to Component. How can i achieve this ?
index.tsx
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
App.tsx
import React from "react";
import logo from "./logo.svg";
import "./App.css";
import { CarsGrid } from "./CarsGrid";
const init = async () => {
try {
const response = await fetch(
"https://www.ag-grid.com/example-assets/row-data.json"
)
return await response.json();
} catch (err) {}
};
function App() {
init().then(result=> {
console.log('Correct Response is printed',result);
return <CarsGrid gridRows={result} />;
});
return (<></>)
}
export default App;
React Component
export function CarsGrid({gridRows}) {
console.log('Data gridRows', gridRows);
})
But results does not get printed in console.log('Data gridRows', gridRows); though response is printed at console.log('Correct Response is printed',result);
Any help is appreciated.
The return in your code:
init().then(result=> {
console.log('Correct Response is printed',result);
return <CarsGrid gridRows={result} />;
});
is returning for the callback passed to then, not as the rendering result of your App function/component.
So the init().then() part is not contributing to the rendering process. This is as if your App code was:
function App() {
return (<></>)
}
And so your CarsGrid function is never executed.
A "react" way would be to rely on a state and to fetch your data in an effect:
function App() {
const [data, setData] = useState();
useEffect(() => {
const init = async () => {
try {
const response = await fetch(
"https://www.ag-grid.com/example-assets/row-data.json"
);
const result = await response.json();
console.log('Correct Response is printed',result);
setData(result);
} catch (err) {}
};
init();
}, []);
return data && <CarsGrid gridRows={data} />;
}
Below is the code located at "Pages/home.js". // localhost:3000/home
import axios from 'axios';
import Section1 from '../components/home-sections/section-1';
const Homepage = ({ show }) => {
const Html = JSON.parse(show.response.DesktopHTML);
const renderSection = () => {
return Html.map((itemData,index)=>{
return(<div key={index}>{itemData.DisplayName}</div>)
})
}
return(
<div>
{ renderSection()}
<Section1 />
</div>
)
}
export const getServerSideProps = async ({ query }) => {
try {
const response = await axios.get(
`https://api.example.com/getHomeSection?title=Section 1`
);
return {
props: {
show: response.data,
},
};
} catch (error) {
return {
props: {
error: error.error,
},
};
}
};
export default Homepage;
Now same code I added into section-1.js and this file is located to "components/home-sections/section-1.js"
Now getServerSideProps is working fine in home.js, but in section-1.js it is not working.
Error: TypeError: show is undefined in section-1.js
You cannot use getServerSideProps in non-page components. You can either pass the prop from Home to HomeSection or create a context so the value can be available globally from the component tree
getServerSideProps can only be exported from a page. You can’t export
it from non-page files.
https://nextjs.org/docs/basic-features/data-fetching#only-allowed-in-a-page-2
getServerSideProps can only be exported from Page components. It will not be run on components imported into a page.
However, you could export a function from the component that returns the props, and call that function from the page's getServerSideProps function.
Create a getServerSideProps function on the component.
// #components/MyComponent.tsx
import { GetServerSidePropsContext } from 'next';
function MyComponent(props: IMyComponentProps) {
return (<div>MyComponent</div>;)
}
MyComponent.getServerSideProps = async (context: GetServerSidePropsContext): Promise<{ props: IMyComponentProps }> => {
return { props: { ... } };
}
export default MyComponent;
In your page's getServerSideProps function, call the component's getServerSideProps function and merge the props from the component with the props from the page.
// mypage.tsx
import MyComponent from '#components/MyComponent';
const Page: NextPageWithLayout = (props: IIndexPageProps) => {
return <MyComponent />;
}
export async function getServerSideProps(context: GetServerSidePropsContext): Promise<{ props: IIndexPageProps }> {
let componentServerSideProps = await MyComponent.getServerSideProps(context);
let otherServerSideProps = { props: { ... } };
return {
props: {
...componentServerSideProps.props,
...otherServerSideProps.props
}
};
}
here's the jist of where I'm stuck (or just read the title for my question).
I have a firebase.js file where I have functions to authenticate. signinGithub, signinGoogle, signinEmail and so forth. The Firebase Auth business logic is in these functions.
I am showing errors with console.log or alert from these functions. The functions are imported into a Component and I don't know how to capture the functions result into the component by somehow setting state from this out-of-component function file.
Here's a basic example:
firebase.js
...
const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider)
const user = res.user
} catch (err) {
alert(err) // ** I want to pass "err" from here to Login
// ** component by updating Logins state for a message
}
}
export {signinWithGitHub}
...
Login.jsx
import React, { useEffect, useState } from "react"
import { useAuthState } from "react-firebase-hooks/auth"
import {
auth,
signInWithGitHub
} from "../lib/firebase"
function Login() {
const [user, loading, error] = useAuthState(auth)
render(
{* Below is the method call from the imported custom firebase function *}
<button onClick={signInWithGitHub}>
Login with GitHub
</button>
)
}
...
I was thinking something like this but I can't fully resolve it in my mind:
Set state in Login.js const [message, setMessage] = useState('')
When the imported signinWithGitHub has an error message --
I'm stuck figuring out how to apply to function message to the state, any ideas?
You can create a custom function inside your Login. jsx file to call the original signInWithGitHub method with a try catch block. And more importantly, you should not use render inside a functional component. Use return to render the JSX in DOM.
firebase.js
export const signInWithGitHub = async () => {
try {
const res = await signInWithPopup(auth, githubProvider);
const user = res.user;
} catch (err) {
throw new Error(err?.message || "Unable to sign in with GitHub");
}
};
Login.jsx
import React, { useEffect, useState } from "react";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth, signInWithGitHub } from "../lib/firebase";
function Login() {
const [user, loading, error] = useAuthState(auth);
const [errorMessage, setErrorMessage] = useState("");
const onLogin = async () => {
try {
await signInWithGitHub();
} catch (err) {
setErrorMessage(err);
}
};
return (
<>
<button onClick={onLogin}>Login with GitHub</button>
{!!errorMessage && <h5>{errorMessage}</h5>}
</>
);
}
I've got a problem with updating data from API in NextJS. I want to increment the value by clicking on the button and then update the API.
My local JSON file is in at http:localhost/api/data
data.js:
export default function handler(req, res) {
res.status(200).json({ value: 0 })
}
I want to update this value from Home component
index.js:
import { useState } from 'react'
export default function Home({data) {
const [currentData, setCurrentData] = useState(data)
return (
<div>
{data.value}
<button onClick={(e) => setCurrentData(currentData.value + 1)}>+</button>
</div>
)
}
export async function getServerSideProps() {
const res = await fetch('http://localhost:3000/api/data')
const data = await res.json()
return {
props: { data },
}
}
Is there any way to do it? If something is unclear feel free to ask :)
I'm attempting to call a Graph QL Query after receiving data from my useEffect hook. I need the data from the response to use in the Query. Hooks however cannot be called conditionally. If I take away the condition however, loadedAnime will be undefined. How do I get around this restraint?
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import AnimeBanner from "../components/AnimeBanner";
import { useHttpClient } from "../Hooks/http-hook";
import { GetAnimeData } from "../GraphQLFunctions";
import { useQuery } from "#apollo/react-hooks";
import gql from "graphql-tag";
const GET_ANIME_INFO = gql`
query GetAnimeInfo($name: String!) {
Media(search: $name) {
title {
romaji
english
native
userPreferred
}
episodes
id
bannerImage
}
}
`;
const Anime = (props) => {
//Logic for getting anime data from mongoDB (episodes, name, cover image)
const { isLoading, error, sendRequest } = useHttpClient();
const [loadedAnime, setloadedAnime] = useState();
const URLTitle = useParams().URLTitle;
useEffect(() => {
const fetchAnime = async () => {
try {
const responseData = await sendRequest(
"http://localhost:5000/api/anime/" + URLTitle
);
setloadedAnime(responseData.animeData[0]);
} catch (err) {
console.log(err);
}
};
fetchAnime();
}, [sendRequest, URLTitle]);
if (isLoading || error) {
return null;
}
//Logic for getting anime data from anilist (Descriptions, tags, banner, trailer, etc.)
const { apiData, apiLoading, apiError } = useQuery(GET_ANIME_INFO, {
variables: {
name: loadedAnime.anime_name,
},
});
if (apiLoading || apiError) {
return null;
}
return <AnimeBanner src={apiData.Media.bannerImage} />;
};
export default Anime;
Short Answer: You can checkout useLazyQuery instead of useQuery.
Documentation link: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
When React mounts and renders a component that calls the useQuery hook, Apollo Client automatically executes the specified query. But what if you want to execute a query in response to a different event, such as a user clicking a button?
The useLazyQuery hook is perfect for executing queries in response to events other than component rendering. This hook acts just like useQuery, with one key exception: when useLazyQuery is called, it does not immediately execute its associated query. Instead, it returns a function in its result tuple that you can call whenever you're ready to execute the query
import React, { useState } from 'react';
import { useLazyQuery } from '#apollo/client';
function DelayedQuery() {
const [dog, setDog] = useState(null);
const [getDog, { loading, data }] = useLazyQuery(GET_DOG_PHOTO);
if (loading) return <p>Loading ...</p>;
if (data && data.dog) {
setDog(data.dog);
}
return (
<div>
{dog && <img src={dog.displayImage} />}
<button onClick={() => getDog({ variables: { breed: 'bulldog' } })}>
Click me!
</button>
</div>
);
}
You can either call the query after the await finishes or you can call your query in another useEffect once you update state after your api call. In general, something like this,
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
// run your query here now that the await has resolved
}, [someDependency])
or
const [state, setState] = useState({})
useEffect(async () => {
const result = await get('/api/blah-blah-blah')
setState(result)
}, [someDependency])
useEffect(() => {
if(state.id) {
// run the query
}
}, [state.someProp])