I created a file Category.js
import React, { useState } from 'react'
export const CategoryData = (props) => {
const [Category, setCategory] = useState('')
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
return Category
}
export default CategoryData
I want to use this Category const in my other component.
I tried to do that with
import CategoryData from '../consts/CategoryData'
and using this function in useEffect of another component. like this.
useEffect(() => {
console.log(CategoryData)
})
But it's not working.
You cannot use hooks directly within functions, unless they are custom hooks which you then cannot invoke inside other hooks but have to follow the rules of hooks to use them
You can restructure your code to implement CategoryData like a custom hook
export const useCategoryData = (props) => {
const [Category, setCategory] = useState('')
useEffect(() => {
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
}, [])
return Category
}
export default useCategoryData;
Now you can use it in your component like
function App () {
const categoryData = useCategoryData();
...
}
SOLUTION2:
Another way to implement this is to not use custom hook but implement a normal function like
export const CategoryData = (props) => {
return fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
})
.catch((error) => {
console.error(error)
})
}
export default CategoryData
and use it like
function App () {
const [categoryData, setCategoryData] = useState(null);
useEffect(() => {
CategoryData.then(res => setCategoryData(res));
}, []); Make sure to provide a dependency list to your useEffect otherwise you will end up in a infinite loop
}
Make it as a custom hook
import React, { useState } from 'react'
export const useCategoryData = (props) => {
const [Category, setCategory] = useState('')
useEffect(()=>{
fetch('https://www.amrutras.com/Category.php')
.then((response) => response.json())
.then((responseJson) => {
{
setCategory(responseJson)
// responseJson.map((item) => Alert.alert(item.Name))
}
// Showing response message coming from server after inserting records.
})
.catch((error) => {
console.error(error)
})
},[])
return {Category}
}
import it like
import {useCategoryData} from '.......'
const {Category} = useCategoryData()
Related
iam new to React and trying to show data from API,
It works at first but after reload i got error " Cannot read properties of undefined (reading 'length')",
any ideas what could it cause ?
thanks
code looks like this:
import React from "react";
import { useEffect, useState } from "react";
const options = {
//options for API call
};
const Ticket = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(
"https://daily-betting-tips.p.rapidapi.com/daily-betting-tip-api/items/daily_betting_coupons?sort=-id",
options
)
.then((res) => res.json())
.then((data) => {
setData(data);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) {
return <p>data is loading...</p>;
}
return (
<div>
<h1>length: {data.data.length}</h1>
<h2></h2>
</div>
);
};
export default Ticket;
You are getting this error because you have data state which is an array but in return you are trying to access data key from the state's data array, which is not there hence it returns the undefined and then you are trying to access the length from undefined.
Instead of data.data.length just use data.length
Use this code. I edited your code. Add a condition when set your data variable
if(data.data) {
setData(data.data)
}
And also change this line
<h1>length: {data.data.length}</h1>
To
<h1>length: {data.length}</h1>
Here is the full code
import React from "react";
import { useEffect, useState } from "react";
const options = {
//options for API call
};
const Ticket = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetch(
"https://daily-betting-tips.p.rapidapi.com/daily-betting-tip-api/items/daily_betting_coupons?sort=-id",
options
)
.then((res) => res.json())
.then((data) => {
if (data.data) {
setData(data.data);
}
})
.catch((err) => {
console.log(err);
})
.finally(() => {
setLoading(false);
});
}, []);
if (loading) {
return <p>data is loading...</p>;
}
return (
<div>
<h1>length: {data.length}</h1>
<h2>Hello world</h2>
</div>
);
};
export default Ticket;
I'm building MERN stack app wherein when logged in as Admin it will render all products in a table and when logged in as Not Admin it will render active products in cards.
I'm trying to do multiple fetch data in my Products.js page
//Products.js
import { Fragment, useEffect, useState, useContext } from "react";
import { Container, Table } from "react-bootstrap";
import ProductCard from "../components/ProductCard";
import UserContext from "../UserContext";
export default function Products() {
const { user } = useContext(UserContext);
const [userProducts, setUserProducts] = useState([]);
const [adminProducts, setAdminProducts] = useState([]);
// FETCH DATA
useEffect(() => {
fetchAdminProducts();
}, []);
useEffect(() => {
fetchUserProducts();
}, []);
const fetchAdminProducts = () => {
fetch("http://localhost:4000/products/all")
.then((res) => res.json())
.then((data) => {
setAdminProducts(data);
});
};
const fetchUserProducts = () => {
fetch("http://localhost:4000/products/")
.then((res) => res.json())
.then((data) => {
setUserProducts(data);
});
};
return (
<Fragment>
<Container>
{user.isAdmin === true ?
(...) :
(...)}
</Container>
</Fragment>
);
}
Am I doing it correctly?
How do I map Admin products in a table and User products in a card?
What is the best approach to fetch multiple data and render it conditionally when logged in?
Thanks for the help guys!
try to check the user role inside the useEffect
//Products.js
import { Fragment, useEffect, useState, useContext } from "react";
import { Container, Table } from "react-bootstrap";
import ProductCard from "../components/ProductCard";
import UserContext from "../UserContext";
export default function Products() {
const { user } = useContext(UserContext);
const [userProducts, setUserProducts] = useState([]);
const [adminProducts, setAdminProducts] = useState([]);
// FETCH DATA
useEffect(() => {
if(user.isAdmin){ // <-- check if is Admin
fetchAdminProducts();
} else{
fetchUserProducts();
}
}, []);
const fetchAdminProducts = () => {
fetch("http://localhost:4000/products/all")
.then((res) => res.json())
.then((data) => {
setProducts(data);
});
};
const fetchUserProducts = () => {
fetch("http://localhost:4000/products/")
.then((res) => res.json())
.then((data) => {
setProducts(data);
});
};
return (
<Fragment>
<Container>
{user.isAdmin === true ?
(...) :
(...)}
</Container>
</Fragment>
);
}
I would recommend having one function rather than 2 and also have like a state that will make that your useEffect go off.
const [state,setState]=useState(null);
const [products,setProducts]=useState([]);
const fetchProducts = () => {
if(user ==="Admin"){
fetch("http://localhost:4000/products/all")
.then((res) => res.json())
.then((data) => {
setProducts(data);
});
setState(true);
}
else{
fetch("http://localhost:4000/products/")
.then((res) => res.json())
.then((data) => {
setProducts(data);
});
setState(false);
}
};
usEffect(()=>{
fetchProducts();
},[state])
Then the return would look like that.
return (
{state ? <Table data=products> : <Card data=products>}
)
Would recommend create a seperate component that you call Table and Card that take in products. This will make your code more neat and easier to manage.
Hope this help.
I have a custom hook where I call 16 subreddits at the time because I want to implement an infinite scroll. When the url page parameter change I want to add the new data to the array witch then I map. But I cant find the right way to do it with typescript. Can some of you guys show me the right way?
The types:
export type Subreddits = [Subreddit];
export type Subreddit = {
id: string;
title: string;
description: string;
};
The Hook:
function useSubreddit() {
let [subredditData, setSubredditData] = useState<any>([]);
const [loadingSubbredits, setLoadingSubreddits] = useState(false);
const [subredditError, setSubredditError] = useState(null);
const dispatch = useDispatch();
const url =
"https://6040c786f34cf600173c8cb7.mockapi.io/subreddits?page=1&limit=16";
useEffect(() => {
setLoadingSubreddits(true);
axios
.get(url)
.then((response) => {
setSubredditData(
(subredditData = [ new Set([...subredditData, ...response.data])])
);
dispatch(setSubredditsData(response.data));
})
.catch((err) => {
setSubredditError(err);
})
.finally(() => setLoadingSubreddits(false));
}, [url]);
return { loadingSubbredits, subredditError, subredditData };
}
export default useSubreddit;
This is the right way to add new item to set:
setSubredditData((
{ subredditData }) => ({
subredditData: new Set(subredditData).add(response.data)
})
);
Change subredditData type to Array
const [subredditData, setSubredditData] = useState<Array>([])
Then use;
setSubredditData(subredditData => [...subredditData, ...response.data])
Ideally you should define the type of response object so Typescript knows what kind of data it has;
.then((response: ResponseType) => {
Working example: codesandbox
import { useState, useEffect } from "react";
import axios from "axios";
function useSubreddit() {
let [subredditData, setSubredditData] = useState({});
const [loadingSubbredits, setLoadingSubreddits] = useState(false);
const [subredditError, setSubredditError] = useState(null);
//console.log(subredditData);
const url =
"https://6040c786f34cf600173c8cb7.mockapi.io/subreddits?page=1&limit=16";
useEffect(() => {
setLoadingSubreddits(true);
axios
.get(url)
.then((response) => {
const result = response.data?.reduce((prev,curr) => ({ ...prev, ...{ [curr.id]: curr }}),{});
setSubredditData((subredditData) => ({ ...subredditData, ...result }));
})
.catch((err) => {
setSubredditError(err);
})
.finally(() => setLoadingSubreddits(false));
}, [url]);
return { loadingSubbredits, subredditError, subredditData:Object.values(subredditData) };
}
export default useSubreddit;
working example: https://codesandbox.io/s/musing-brown-9nf7ne?file=/src/App.js
I found the solution
function useSubreddit() {
const [subredditData, setSubredditData] = useState<Array<any>>([])
const [loadingSubbredits, setLoadingSubreddits] = useState(false);
const [subredditError, setSubredditError] = useState(null);
const dispatch = useDispatch();
const url =
"https://6040c786f34cf600173c8cb7.mockapi.io/subreddits?page=1&limit=4";
useEffect(() => {
setLoadingSubreddits(true);
axios
.get(url)
.then((response: SubredditsResponse) => {
setSubredditData(Array.from( new Set([ ...subredditData, ...response.data])))
dispatch(setSubredditsData(response.data));
})
.catch((err) => {
setSubredditError(err);
})
.finally(() => setLoadingSubreddits(false));
}, [url]);
return { loadingSubbredits, subredditError, subredditData };
}
export default useSubreddit;
Thank you for your help guys
I was wondering how can I call an API that requires data from other API. In my case I want to get the coordinates; lattitude and longitute and make another API call with these to retrieve the information I need.
Here is App.js file
import React from 'react';
import './App.css';
import CityInput from "./components/CityInput";
import {Container} from 'react-bootstrap';
import UseFetch from "./hooks/useFetch";
import {API_BASE_URL, API_KEY} from "./apis/config";
import WeatherList from "./components/WeatherList";
const App = () => {
const {data, error, isLoading, setUrl} = UseFetch();
const getContent = () => {
if(error) return <h2>Error when fetching: {error}</h2>
if(!data && isLoading) return <h2>LOADING...</h2>
if(!data) return null;
return <WeatherList weathers={data.list}/>
};
return (
<Container className="App">
{/* user types a city and clicks search*/}
<CityInput onSearch={(city) => setUrl(`${API_BASE_URL}/data/2.5/forecast?q=${city}&appid=${API_KEY}&units=metric`)} />
{getContent()}
</Container>
);
}
export default App;
and here is my UseFetch.js file
import {useState, useEffect} from 'react';
import {API_BASE_URL, API_KEY} from "../apis/config";
const UseFetch = (initialUrl) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const [url, setUrl] = useState(initialUrl);
useEffect(() => {
if(!url) return;
setIsLoading(true);
setData(null);
setError(null);
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
setData(data);
console.log(data);
console.log(data.city.coord.lat);
console.log(data.city.coord.lon);
})
.catch((error) => {
setIsLoading(false);
setError(error);
});
//
// console.log('HIIIIII'+ data);
// fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
// .then((response) => response.json())
// .then((data2) => {
// setIsLoading2(false);
// setData2(data2);
// console.log(data2);
// })
// .catch((error2) => {
// setIsLoading2(false);
// setError2(error);
// });
}, [url]);
return { data, error, isLoading, setUrl};
};
export default UseFetch;
I want to retrieve lantitude and lontitude so i can make another fetch
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
But this doesnt seem to work.
I'm using these API for reference:
https://openweathermap.org/forecast5
https://openweathermap.org/api/one-call-api
Call it right after the Initial Api sends back the response for example:
fetch(APIURL)
.then(response => {
/** do any operations using received response data **/
/** Calling second api **/
fetch(API_URL_ + response.data.url)
.then(data => {
setData(data)
})
.catch(error)
}).catch(error)
UseFetch.js
import {useState, useEffect} from 'react';
import {API_BASE_URL, API_KEY} from "../apis/config";
const UseFetch = (initialUrl) => {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(null);
const [url, setUrl] = useState(initialUrl);
useEffect(() => {
if(!url) return;
setIsLoading(true);
setData(null);
setError(null);
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
setData(data);
console.log(data);
console.log(data.city.coord.lat);
console.log(data.city.coord.lon);
console.log('HIIIIII'+ data);
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${data.city.coord.lat}&lon=${data.city.coord.lon}&exclude=minutely&appid=${API_KEY}`)
.then((response) => response.json())
.then((data2) => {
setIsLoading2(false);
setData2(data2);
console.log(data2);
})
.catch((error2) => {
setIsLoading2(false);
setError2(error);
});
})
.catch((error) => {
setIsLoading(false);
setError(error);
});
}, [url]);
return { data, error, isLoading, setUrl};
};
export default UseFetch;
You can try to chain another .then() after save the first response in a variable??Something like:
let anyVariable;
fetch(url)
.then((response) => response.json())
.then((data) => {
setIsLoading(false);
if(data.cod >= 400) {
setError(data.message);
return;
}
anyVariable = data.city.coord
})
.then(() => {
fetch(`${API_BASE_URL}/data/2.5/onecall?lat=${anyVariable.lat}&lon=${anyVariable.lon}&exclude=minutely&appid=${API_KEY}`)
.then((response) => response.json())
.then((data2) => {
setIsLoading2(false);
setData2(data2);
console.log(data2);
})
})
Any way i think it will be cleaner and better performing to use axios and async await. Also notice that useState is asynchronous.
How can I add sorting for API returning a JSON array? I'm new to Redux. I have installed the redux. Could someone tell me what's the best method to follow?
Thanks for your help.
import React, { useState, useEffect } from "react";
import Post from "../../Components/Post/Post";
import axios from "axios";
const HomePage = () => {
const [posts, setPosts] = useState("");
let config = { Authorization: "............." };
const url = "..........................";
useEffect(() => {
AllPosts();
}, []);
const AllPosts = () => {
axios
.get(`${url}`, { headers: config })
.then((response) => {
const allPosts = response.data.articles;
console.log(response);
setPosts(allPosts);
})
.catch((error) => console.error(`Error: ${error}`));
};
return (
<div>
<Post className="Posts" posts={posts} />
</div>
);
};
export default HomePage;
You don't have redux here. Do you need it?
If you want to sort result and save sorted results to state:
...
.then((response) => {
const allPosts = response.data.articles;
// sort the result here
const sortedPosts = allPosts.sort((a,b) =>
// comparer
);
setPosts(sortedPosts);
})
...