I am not able to map over the data which is stored in the redux array.
This action creator fetches the data and calls the dispatch action.
import { checkStatus, getTours, checkLoading } from "../../features/tourSlice";
export const getData = () => {
return async (dispatch) => {
try {
dispatch(checkLoading(true));
const res = await fetch(`http://localhost:8080/backpack/api/r1/tours`);
if (!res.ok) {
throw new Error(`Error while connecting with server`);
}
const data = await res.json();
dispatch(checkLoading(false));
dispatch(getTours(data));
} catch (error) {
dispatch(
checkStatus({
title: `Error`,
message: `Servers are down or Error while connecting please try again later`,
})
);
}
};
};
calling in app.js
function App() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(getData());
}, [dispatch]);
while using this component gives nothing.
const data = useSelector((state) => state.Tour.tours);
but doing console.log of data gives the array but gives nothing when maping over
small example of mapping=>
<main className="main">
<div className="card-container">
{data.map((item) => (
<div className="card" key={item._id}>
{console.log(item)}
<div className="card__header">
<div className="card__picture">
<div className="card__picture-overlay"> </div>
<img
src=`${item.image}`
alt="Tour 1"
className="card__picture-img"
/>
Related
I want to display a list of products based on specific categories fetched from api, like below:
const API = "https://dummyjson.com/products";
const ProductsList = () => {
const { cate } = useParams(); //here I am getting category from Viewall component
const { getFilterProducts, filter_products } = useFilterContext();
useEffect(() => {
getFilterProducts(`${API}/category/${cate}`);
}, [cate]);
return (
<div className="mx-2 mt-2 mb-16 md:mb-0 grid grid-cols-1 md:grid-cols-12">
<div className="h-9 w-full md:col-span-2">
<FilterSection />
</div>
<div className="md:col-span-10">
<ProductListDetails products={filter_products} />
</div>
</div>
);
};
My FilterContextProvider is as follows
const initialState = {
filter_products: [],
};
const FilterProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const { products } = useAppContext();
const getFilterProducts = async (url) => {
dispatch({ type: "FILTERS_LOADING" });
try {
const res = await fetch(url);
const data = await res.json();
if (!res.ok) {
var error = new Error("Error" + res.status + res.statusText);
throw error;
}
dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: data.products });
} catch (err) {
dispatch({ type: "FILTERS_ERROR", payload: err.message });
}
};
return (
<FilterContext.Provider value={{ ...state, getFilterProducts }}>
{children}
</FilterContext.Provider>
);
};
I tried using this simple approach in my ProductList component to clean up:
useEffect(() => {
let inView = true;
getFilterProducts(`${API}/category/${cate}`);
return () => {
inView = false;
};
}, [cate]);
But it does not seem to work. When I move to the ProductList component, it first displays data of my previous filer_products value, then after a few fractions of seconds, updates the data and shows current data.
I am expecting that when the ProductList component unmounts, its rendered data should vanish, and when I navigate it again, it should render the current data directly, not after a fraction of seconds.
As you explained, I assume your context is wrapping your routes, and it's not re-rendering when switching between pages. A simple solution is to have a loader in ProductsList, wait for the new data to replace the old, and have the user notice what's happening with a loader:
const ProductsList = () => {
const { cate } = useParams(); //here I am getting category from Viewall component
const { getFilterProducts, filter_products } = useFilterContext();
const [loading, setLoading] = useState(true);
useEffect(() => {
getFilterProducts(`${API}/category/${cate}`).then(() => {
setLoading(false);
});
}, [cate]);
if (loading) {
return <p>Hang tight, the data is being fetched...</p>;
}
return (
<div className="mx-2 mt-2 mb-16 md:mb-0 grid grid-cols-1 md:grid-cols-12">
<div className="h-9 w-full md:col-span-2">
<FilterSection />
</div>
<div className="md:col-span-10">
<ProductListDetails products={filter_products} />
</div>
</div>
);
};
If you need to clear your store in a clean-up function, you can add dispatch as part of your context value, grab it in ProductsList and call it like so:
<FilterContext.Provider value={{ ...state, getFilterProducts, dispatch }}>
{children}
</FilterContext.Provider>
const { getFilterProducts, filter_products, dispatch } = useFilterContext();
useEffect(() => {
getFilterProducts(`${API}/category/${cate}`);
return () => {
dispatch({ type: "LOAD_FILTER_PRODUCTS", payload: {} });
};
}, [cate]);
today i have a problem with my searchbar.
const [posts, setPosts] = useState(null)
const [searchTerm, setSearchTerm] = useState("")
useEffect(() => {
const loadPosts = async () => {
try {
const post = await getAllPosts()
setPosts(post)
} catch (e) {
alert("Couldn't load posts")
}
}
loadPosts()
}, [])
return (
<div>
<input type={"text"} placeholder="Search..." onChange={event => {
setSearchTerm(event.target.value)
}}/>
</div>
)
}
This is my Searchbar Component. In the Index file, did i gave a props with.
const [posts, setPosts] = useState([])
const [searchTerm, setSearchTerm] = useState("")
useEffect(() => {
const loadPosts = async () => {
try {
const post = await getAllPosts()
setPosts(post)
} catch (e) {
alert("Couldn't load posts")
}
}
loadPosts()
}, [])
return (
<div className={styles.posts}>
<h1>Market-place Valando</h1>
<SearchList title={posts.filter(post => {
if (post.title.toLowerCase().includes(searchTerm.trim().toLowerCase()) && searchTerm.trim() !== "") {
return post.title
}
}).map(titles => {
{
{titles.title}
}
}
)}/>
{
posts.map(post => {
return (
<div key={post.id} className={styles.key}>
<h1>{post.title}</h1>
<Image width={1000} height={1000} src={post.image}/>
<p>Price: {post.price}.-</p>
<p>Description: {post.description}</p>
<Link href={`/posts/${post.id}`} passHref>
<a>Read more</a>
</Link>
</div>
)
})
}
</div>
)
}
I have a db.json file that i connected with an API File. In this Api File i made all the fetch stuff. This shouldnt be the problem. I think the problem is, that the filter doesnt work properly, with the titels.
You are correct, JavaScript filter does not return specific property values, but it returns the top entries of the array, a.k.a posts. So return post.title or return true will yield the same result. However, the problem in your code appears to be that you are not returning anything from the map function. All you need to do is to change it to the following:
.map(post => post.title)
Here I am trying to get productList from MySQL database and for each product object I am assigning new property - imageURL via getImages(). When I log productList to console, there is property imageURL with correct url. Problem is, when I try to map it, it shows nothing. Why?
const storageRef = firebase.storage().ref("/assets")
const [productList, setProductList] = useState([])
useEffect(() => {
Axios.get("http://localhost:3001/product/get").then((response) => {
setProductList(response.data)
})
}, [])
useEffect(() => {
getImages(productList)
}, [productList])
const getImages = (array) => {
array.forEach((item) => {
storageRef.child(`${item.bannerImage}`).getDownloadURL().then((url) => {
item.imageURL = url
})
})
}
My map function:
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<h2>IMAGE: {val.imageURL}</h2>
</div>
</div>
)
})}
Problems:
You are not setting productList back in getImages function. You are just iterating over array.
getDownloadURL is a async function, you should not use it inside loop. The best way to do this is through a recursive function. But you can also do this as below:
Solution
Your getImage function
const getImage = async (bannerImage) => {
const url = await storageRef.child(bannerImage).getDownloadURL();
return url;
}
then your map function
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<h2>IMAGE: {getImage(val.bannerImage)}</h2>
</div>
</div>
)
})}
I would suggest you create another small component for your image rendering and handle async for getDownloadURL behaviour inside that component
function ProductImage({bannerImage}) {
const [imageUrl, setImageUrl] = useState('')
useEffect(() => {
async function getImage(bannerImage) {
const url = await bannerImage.getDownloadURL()
setImageUrl(url)
}
getImage(bannerImage)
}, [bannerImage])
return imageUrl ? <h2>IMAGE: {imageUrl}</h2> : '...Loading'
}
And use this component in your main component
{productList.map((val) => {
return (
<div key={val.id} className="product">
<div className="item">
<h1>Product title: {val.title}</h1>
<h2>Price: {val.price}</h2>
<h2>Quantity: {val.quantity}</h2>
<ProductImage bannerImage={val.bannerImage} />
</div>
</div>
)
})}
I want to create a simple layout where the user can choose a dynamic dropdown that retrieves data based on an API call using React.js and Django as backend, but however, I want to pre-render category from the database and let the user choose which Industry, and change the layout data accordingly
async function fetchFeed(domain) {
return api.get(`http://localhost:8002/api/v1/xxxx/list/?domain=${domain}`);
}
async function fetchDomain() {
return api.get('http://localhost:8002/api/v1/xxxx/domain/'); # return all domains
}
export default function Board () {
const [isModalOpen, setModalIsOpen] = useState(false);
const [users, setUsers] = useState([]);
const [responseData, setResponseData] = useState([])
const [domains, setDomains] = useState([]);
// fetches data
const fetchData = (domain) => {
fetchFeed(domain)
.then((response)=>{
setResponseData(response.data.results)
})
.catch((error) => {
console.log(error)
})
}
const handleOnClick = async (data) => {
try {
setUsers(data);
// Now that the data has been fetched, open the modal
setModalIsOpen(true);
} catch (err) {
console.error("failed", err);
}
};
useEffect(() => {
fetchData();
}, []);
return (
<div className="container content">
<select>options</select>
{responseData.map((data) => (
<div className="col" key={data.t_id}>
<div className="row">{data.tactic_name}</div>
{data.data.map((item) => (
<div className="row" key={item._id} onClick={() => handleOnClick(item)}><span>{item.title}</span></div>
))}
</div>
))}
{isModalOpen && <Modal onRequestClose={() => setModalIsOpen(false)} data={users}/> }
</div>
);
}
I call a get request to my api, and then register them to my state with this:
useEffect(() => {
fetchPosts()
},)
const [posts, setPosts] = useState([])
const fetchPosts = async () => {
const data = await fetch('http://localhost:3000/posts/')
const posts_data = await data.json()
setPosts(posts_data)
}
I even tried the axios approach:
await axios.get('http://localhost:3000/posts/')
.then(res => {
setPosts(res.data)
console.log(posts)
})
If I console.log posts_data and posts, it gives me the Object I got from my api:
[{title: "Sample post", desc: "sample desc"}, {...}]
But whenever I iterate and display it:
<div>
{posts.map(post => {
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
})}
</div>
It doesn't show up on the page. I even tried adding that random string there asdjasdljaskldjs and it doesn't show too. The data is received and stored, but I wonder why it doesn't display.
Entire component code
import React, {useState, useEffect} from 'react'
import axios from 'axios'
function Posts() {
useEffect(() => {
fetchPosts()
},)
const [posts, setPosts] = useState([])
const fetchPosts = async () => {
await axios.get('http://localhost:3000/posts/')
.then(res => {
setPosts(res.data)
console.log(posts)
})
// const data = await fetch('http://localhost:3000/posts/')
// const posts_data = await data.json()
// setPosts(posts_data)
// console.log(posts)
}
return (
<div className="container-fluid col-lg-7 mt-3">
<h1>POSTS</h1>
<div>
{posts.map(post => {
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
})}
</div>
</div>
)
}
export default Posts
I also noticed when I console.log the posts_data or posts, it keeps printing over and over again while you're on the page. Is that normal?
Your mapping function isn't returning the JSX. Change your return to:
return (
<div className="container-fluid col-lg-7 mt-3">
<h1>POSTS</h1>
<div>
{posts.map(post => (
<div>
<p>{post.title}</p>
<h1>asdjasdljaskldjs</h1>
</div>
))}
</div>
</div>
)
You need to surround the returned JSX with parens, not {}, or you need a return before the {}.