so im doing a little application of a pokedex utilizing the pokeapi to test react query, what i am trying to do is first fetch the data on mount and after that, if i use the search button the data already fetched to change something like this
fetch on mount
and the search
search fetch
something easy to do with useState but i am having problems with react query
i got something like this
pokedex
at the mount of the component i have this
export const fetchPokemon = async (URL: string) => {
const result = await axios.get<pokemon>(URL);
return result;
};
export const fetchPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=20&offset=0s";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
export const useAllPokemons = () => {
return useQuery({
queryKey: ["pokemons"],
queryFn: fetchPokemons,
});
};
const { data, isLoading } = useAllPokemons();
works great but now i want to search pokemons with a search button like in the image and to replace the initial data that i already fetch so only the data that i searched appears so i did this
export const fetchAllPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=100";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
let { data, refetch } = useQuery({
queryKey: ["pokemons"],
queryFn: fetchAllPokemons,
enabled: false,
select: (data) => {
const pokemonData = data.map((pokemon) => {
if (pokemon.data.name.startsWith("char")) {
return pokemon;
}
});
return pokemonData;
},
});
<button
onClick={() => {
refetch();
}}
>
asd
</button>
and nothing happens, but when i open the console the data is changing but then again returns to the initial fetch
I guess you should use the onSuccess:
const {data} = useQuery("fetchData", fetchData, {
onSuccess: (data) => {
// do something with your data and return
}
});
Related
my code
export const getServerSideProps: GetServerSideProps = async () => {
const ref = collection(db, "books");
const results = [];
const unsub = onSnapshot(ref, (snapshot) => {
snapshot.forEach((doc) => {
results.push({ ...doc.data(), id: doc.id });
});
//here i get the results
console.log(results)
});
// here is empty
console.log(results)
return {
props: {
books: results,
},
};
};
I'm trying to get the real time data from firestore database on the getServerSideProps function, inside the snapshot I can get the results, but when it's outside the array it's empty and I can't pass to props.
Instead of using onSnapshot I would use getDocs (you need to import from 'firebase/firestore'):
export const getServerSideProps: GetServerSideProps = async () => {
const [results, setResults] = useState([]);
const ref = collection(db, 'books');
const snapshot = await getDoc(docRef);
articlesSnapshot.forEach((doc) => {
setResults((oldArray) => [...oldArray, doc.data()]);
});
return {
props: {
books: results,
},
};
};
I was also stucked in the same problem.
Here we should use get() instead of snapshot cause next will take care of your updated data under the hood.
so rewrite your code like this
export async function getServerSideProps(context) {
const resultref = await db.collection("books").get();
const results = resultRef.docs.map((doc)=>({
id: doc.id,
resultData: doc.data()
}))
return {
props: { books: JSON.parse(JSON.stringify(results)) },
}
}
so map function will return the new array in variable results
which is then deep copied and passed as props
I hope this will give you the desired result 😀
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 need to call a query when submit button is pressed and then handle the response.
I need something like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(res) // handle response
}
Try #1:
const [checkEmail, { data }] = useLazyQuery(CHECK_EMAIL)
const handleSubmit = async () => {
const res = await checkEmail({ variables: { email: values.email }})
console.log(data) // undefined the first time
}
Thanks in advance!
This works for me:
const { refetch } = useQuery(CHECK_EMAIL, {
skip: !values.email
})
const handleSubmit = async () => {
const res = await refetch({ variables: { email: values.email }})
console.log(res)
}
After all, this is my solution.
export function useLazyQuery<TData = any, TVariables = OperationVariables>(query: DocumentNode) {
const client = useApolloClient()
return React.useCallback(
(variables: TVariables) =>
client.query<TData, TVariables>({
query: query,
variables: variables,
}),
[client]
)
}
You could also use the onCompleted option of the useLazyQuery hook like this:
const [checkEmail] = useLazyQuery(CHECK_EMAIL, {
onCompleted: (data) => {
console.log(data);
}
});
const handleSubmit = () => {
checkEmail({ variables: { email: values.email }});
}
In case someone wants to fetch multiple apis at single load, it could be achieved like this.
On Demand Load > e.g. onClick, onChange
On Startup > e.g. useEffect
import { useLazyQuery } from "#apollo/client";
import { useState, useEffect } from "react";
import { GET_DOGS } from "../../utils/apiUtils";
const DisplayDogsLazy = () => {
const [getDogs] = useLazyQuery(GET_DOGS);
const [data, setData] = useState([]);
useEffect(() => {
getAllData();
}, []);
const getAllData = async () => {
const response = await getDogs();
console.log("Awaited response >", response);
};
const handleGetDogsClick = async () => {
const response = await getDogs();
setData(response.data.dogs);
};
return (
<>
<button onClick={handleGetDogsClick}>Get Dogs</button>
{data?.length > 0 && (
<ul>
{data?.map((dog) => (
<li key={dog.id} value={dog.breed}>
{dog.breed}
</li>
))}
</ul>
)}
</>
);
};
export default DisplayDogsLazy;
I try to store data with AsyncStorage after fetch
const [items, setItems] = useState([]);
const fetchData = async () => {
try {
const response = await url.get(`/../`);
data = response.data;
if (data.length) {
setItems(data)
storeData();
}
} catch(error) {
console.log(error)
}
};
const storeData = async () => {
try {
const data = JSON.stringify(items);
await AsyncStorage.setItem(STORAGE_KEY, data);
} catch (error) {
alert('Error')
}
};
I call function fetchData with button. When i click first time, Asyncstorage store empty array.
On the second click it store an array.
<Button title="fetch data" onPress={fetchData} />
As much as i understood, it works asynchronously, but what should i change in this code to store data at first click?
setItems(data) is async , so you cant guarentee when the data will be set to state, a rather better approach would be like this :
const [items, setItems] = useState([]);
const fetchData = async () => {
try {
const response = await url.get(`/../`);
data = response.data;
if (data.length) {
setItems(data)
storeData(data);
}
} catch(error) {
console.log(error)
}
};
const storeData = async (data) => {
try {
const data1 = JSON.stringify(data);
await AsyncStorage.setItem(STORAGE_KEY, data1);
} catch (error) {
alert('Error')
}
};
here im passing the data to storeData and setting that value, hope it helps. please feel free for doubts
My app uses React, Redux and Thunk.
Before my app renders I wish to dispatch some data to the store.
How can I make sure the ReactDOM.render() is run after all dispatches has finished?
See my code below
index.js
const setInitialStore = () => {
return dispatch => Promise.all([
dispatch(startSubscribeUser()),
dispatch(startSubscribeNotes()),
]).then(() => {
console.log('initialLoad DONE')
return Promise.resolve(true)
})
}
store.dispatch(setInitialStore()).then(()=>{
console.log('Render App')
ReactDOM.render(jsx, document.getElementById('app'))
})
Actions
export const setUser = (user) => ({
type: SET_USER,
user
})
export const startSubscribeUser = () => {
return (dispatch, getState) => {
const uid = getState().auth.id
database.ref(`users/${uid}`)
.on('value', (snapshot) => {
const data = snapshot.val()
const user = {
...data
}
console.log('user.on()')
dispatch(setUser(user))
})
}
}
export const setNote = (note) => ({
type: SET_NOTE,
note
})
export const startSubscribeNotes = () => {
return (dispatch, getState) => {
database.ref('notes')
.on('value', (snapshot) => {
const data = snapshot.val()
const note = {
...data
}
console.log('note.on()')
dispatch(setNote(note))
})
}
}
My log shows
"initialLoad DONE"
"Render App"
...
"user.on()"
"note.on()"
What I expect is for user.on() and note.on() to be logged before initialLoad DONE and Render App
Many thanks! /K
I'm pretty sure this is because startSubscribeUser and startSubscribeNotes don't return a function returning a promise.
Then, what happens in this case, is that the database.ref is not waited to be completed before executing what's in the next then.
I don't know exactly what that database variable is, but this should work :
return new Promise(resolve => {
database.ref(`users/${uid}`)
.on('value', (snapshot) => {
const data = snapshot.val()
const user = {
...data
}
console.log('user.on()')
dispatch(setUser(user))
resolve()
})
})