How to do multiple fetch data and conditional render in react - reactjs

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.

Related

Issue with fetching data

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;

How can I add sorting using Redux?

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);
})
...

Why my data from an api is still undefined?

I'm trying to fetch some data from an api and display it in jsx.First I get the users geolocation,then I call the fetch function which uses the users geolocation data to request the data from an api , afterwards
the received data from an api is used to set the weatherData state.The final step is where conditional rendering is used to show the h1 element depending if the state is defined or not.The problem is that my weatherData is always undefined,and when I try to display it returns as undefined error.Why is my weatherData undefined?
import react from "react";
import {useState} from "react";
import {useEffect} from "react";
const MainWeather=()=>{
{/*State for storing geolocation data*/}
const [status, setStatus] = useState(null);
const [weatherData,setWeatherData]=useState('');
{/*Fetches the data from an api*/}
const fetchData=(link)=>{
fetch(link)
.then(res => res.json())
.then(
(result)=>{
{/*Sets the weather data object*/}
setWeatherData(result);
console.log(result);
setStatus('data set');
},
(error)=>{
console.log(error)
}
)
}
{/*Retrieves the location from geolocation api*/}
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus('Geolocation is not supported by your browser');
} else {
navigator.geolocation.getCurrentPosition((position) => {
{/*Calls the fetch function to get the data from an api*/}
fetchData(`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`);
}, () => {
setStatus('Unable to retrieve your location');
});
}
}
{/*Calls getLocation function on the first render*/}
useEffect(()=>{getLocation()},[])
console.log(status);
return(
<div>
{weatherData == 'undefined' ?
<h1>undefined</h1> :
<h1>{weatherData.current.temp}</h1> }
</div>
)
}
export default MainWeather;
I checked the code, what you have implemented is correct , if you are using a mac you should allow browser to fetch location , in windows a popup will come to allow it , might be browser issue check it again
still I made few changes in below the above code , just refer to it
import { useState, useEffect } from "react";
const MainWeather = () => {
const [status, setStatus] = useState(null);
const [weatherData, setWeatherData] = useState("");
const fetchData = (link) => {
fetch(link)
.then((res) => res.json())
.then(
(result) => {
setWeatherData(result);
setStatus("data set");
},
(error) => {
console.log(error);
}
);
};
const getLocation = async () => {
if (!navigator.geolocation) {
setStatus("Geolocation is not supported by your browser");
} else {
navigator.geolocation.getCurrentPosition(
(position) => {
fetchData(
`https://api.openweathermap.org/data/2.5/onecall?lat=${position.coords.latitude}&lon=${position.coords.longitude}&exclude={part}&appid=0ea4f961aae42bfa56f75ca058577e1e&units=metric`
);
},
() => {
setStatus("Unable to retrieve your location");
}
);
}
};
useEffect(() => {
getLocation();
}, []);
return (
<div>
{!weatherData ? (
<h1>{status}</h1>
) : (
<h1>{weatherData?.current?.temp} ℃ </h1>
)}
</div>
);
};
export default MainWeather;
You can refer to this codesandbox

How to export const to other components react?

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()

Correct way to use useEffect() to update when data changes

The useEffect below renders, fetches data, and displays it once (using an empty array for 2nd parameter in useEffect).
I need it to rerun useEffect everytime the user changes data to the database (when user uses axios.post).
What i've tried
using [tickets], but that just causes the useEffect to run infinitly
also using [tickets.length] and [tickets, setTickets]
trying to use props as parameter but didnt find anything useful
import React, { useState, createContext, useEffect } from "react";
import axios from "axios";
export const TicketContext = createContext();
export const TicketProvider = (props) => {
console.log(props);
const [tickets, setTickets] = useState([]);
useEffect(() => {
getTickets();
console.log("1", { tickets });
}, []);
const getTickets = async () => {
const response = await axios.get("http://localhost:4000/tickets/");
setTickets(response.data);
};
return <TicketContext.Provider value={[tickets, setTickets]}>{props.children}
</TicketContext.Provider>;
};
import React from "react";
import { useState, useEffect, useContext } from "react";
import Ticket from "../Ticket";
import { TicketContext } from "../contexts/TicketContext";
import AddBacklog from "../addData/AddBacklog";
const TicketDisplay = (props) => {
const [tickets, setTickets] = useContext(TicketContext);
return (
<div className="display">
<p>Antony Blyakher</p>
<p>Number of Tickets: {tickets.length}</p>
<div className="backlog">
<h1>Backlog</h1>
{tickets.map((currentTicket, i) => (
<div className="ticketBlock">
<Ticket ticket={currentTicket} key={i} />
</div>
))}
</div>
</div>
);
const AddBacklog = (props) => {
const [tickets, setTickets] = useState("");
...
axios.post("http://localhost:4000/tickets/add", newTicket).then((res) => console.log(res.data));
setTickets((currentTickets) => [...currentTickets, { name: name, status: "backlog", id: uuid() }]);
};
You'll need to watch for tickets and return if it has data to not cause infinite loop:
useEffect(() => {
if (tickets.length) return // so, we call just once
getTickets();
console.log("1", { tickets });
}, [tickets]);
const fetchData = () => {
axios.get("http://localhost:7000/api/getData/").then((response) => {
console.log(response.data);
if (response.data.success) {
SetIsLoading(false);
}
setDataSource(response.data.data);
});
};
useEffect(() => {
fetchData();
if (fetchData.length) fetchData();
}, [fetchData]);
by this you can fetch the data in real-time as any change in data occurs.

Resources