Problem using map method with react (posts.map is not a function) - javascript

i'm having an error while trying to map an array (code below). The error i'm receiving is : Uncaught (in promise) TypeError: posts.map is not a function.
I have compared it to one of my old project, and it worked perfectly on the older one. Thanks in advance
const MainContent = () => {
const openModal = () => {
const modal = document.querySelector(".modal");
modal.style.display = "block";
};
const [posts, setPosts] = useState([]);
const getPosts = async () => {
const response = await fetch('http://localhost:5000/api/post/');
const posts = await response.json();
setPosts(posts);
console.log(posts)
}
useEffect(() => {
getPosts();
}, []);
return (
<section className="main-content-section">
<button className="button post-btn" onClick={openModal}>
Créer un nouveau post
</button>
<ModalPost style={{ display: "none" }} />
<div className="post-container">
{posts.map((post) => {
const { id, title, content, author } = post;
return (
<div className="post-content" key={id}>
<div className="post-content__user-info">
<div className="post-content__photo-container">
</div>
<div className="post-content__name">
<h3>{author}</h3>
</div>
</div>
<div className="post-content__content">
<h3>{title}</h3>
<p>{content}</p>
</div>
<div className="post-content__like-box">
<BiHeart className="like-heart like" size={26} />
<RiDislikeLine className="dislike-heart dislike" size={26} />
</div>
</div>
);
})}
</div>
</section>
);
};

Just add if statement before mapping post array. The reason is that posts array does not get data immediately causing map function to fail. if condition will wait until posts array actually contains data
{
(posts && posts.length > 0) ?
(posts.map((post) => {})
: (<p> loading... </p>)
}

const getPosts = async () => {
const response = await fetch('http://localhost:5000/api/post/');
const posts = await response.json();
setPosts(posts);
console.log(posts)
}
Here, the result of response.json() is going to be an object, and not an array. Probably, you have to look inside the object and find the posts array.

Related

Struggling to filter by category properly

import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
async function setCompany(companyName) {
await getData();
const newData = data.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
Sooo... basically I'm trying to filter the array returned by Fetch and have it display only the category I want (I called it "company instead of category in my code") depending on which button I click as shown in the "nav-buttons" div down in the code.
The first time I click on a button it works fine, but the second time it doesn't show anything as if it's filtering from an already filtered array which return no results obviously.
update these two methods with these two lines:
async function setCompany(companyName) {
const response=await getData(); //THIS ONE
const newData = response.filter((info) => info.company === companyName);
setData(newData);
}
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
return data;// And THIS ONE
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
// Get the oportunity to learn about promises, and you will save so much time. ;)
import React, { useEffect, useState } from "react";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [companyName, setCompanyName] = useState("");
async function setCompany(companyName) {
getData();
const newData = setData(newData);
}
function getData(companyName) {
setCompanyName(companyName);
fetch(url)
.then((res) => res.json())
.then((info) => {
console.log(info);
return companyName
? info.filter((info) => info.company == companyName)
: info;
})
.then((res) => {
console.log(res);
return setData(res);
})
.catch((err) => {
setLoading(false);
console.error(`ERROR ==> ${err}`);
});
}
useEffect(() => {
getData();
}, []);
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{data.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => getData("TOMMY")}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => getData("BIGDROP")}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => getData("CUKER")}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
you don't need to call the same API on each filter as it returns same data if I'm not wrong.
you can filter the data with the derived state, by storing the selected company in state i.e., on each render it calculates based on the selected company.
use the filtered data to render finally.
Here is the full e.g.
import React, { useEffect, useState } from "react";
import Loading from "./Loading";
function App() {
const url = "https://course-api.com/react-tabs-project";
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
const [selectedCompany, setSelectedCompany] = useState(""); // store the company on click
const filteredData = selectedCompany ? data.filter(info=> info.company === selectedCompany) : data; // filter data based on selected company
async function getData() {
try {
const response = await fetch(url);
const data = await response.json();
setData(data);
setLoading(false);
} catch (err) {
setLoading(false);
console.error(`ERROR ==> ${err}`);
}
}
useEffect(() => {
getData();
}, []);
if (loading) {
return <Loading></Loading>; // simple loading screen
}
return (
<main>
<div className="top-wrapper">
<h2>Experience</h2>
<div className="underline"></div>
</div>
{filteredData.map((item) => {
const { id, order, title, dates, duties, company } = item;
return (
<article key={id}>
<h3>{title}</h3>
<span className="company">{company}</span>
<p>{dates}</p>
<ul>
{duties.map((duty, index) => {
return <li key={index}>{duty}</li>;
})}
</ul>
<button>MORE INFO</button>
</article>
);
})}
<div className="nav-buttons">
<button
onClick={() => {
setSelectedCompany("TOMMY");
}}
className="nav-btn"
>
TOMMY
</button>
<button
onClick={() => {
setSelectedCompany("BIGDROP");
}}
className="nav-btn"
>
BIGDROP
</button>
<button
onClick={() => {
setSelectedCompany("CUKER");
}}
className="nav-btn"
>
CUKER
</button>
</div>
</main>
);
}
export default App;
try putting a check case before filter to insure that your array isn't empty.
async function setCompany(companyName) {
await getData();
{data ?
const newData = data.filter((info) => info.company === companyName);
:
null}
setData(newData);
}
I think part of your issue is when your calling get data on button click your state isn't set before running the filter logic. I would look over your functional logic and ask yourself is this the best way to do this and am i trying to filter before or after my response.

Searchbar cant filter JSON File Javascript

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)

SaveData is not output and instanceRef null

I have a problem with react-editor-js library. I'm trying to get data from it, but it doesn't output and when I check instanceRef it's null. Here is the code.
const Create = () => {
const instanceRef = React.useRef(null);
let data = { '1': 'test' }
async function handleSave() {
const savedData = await instanceRef.current.save()
console.log(savedData)
}
return (
<div>
<Header Name='Создание'/>
<Menu/>
<div className="editor">
<ReactEditorJS
instanceRef={(instance) => (instanceRef.current = instance)}
tools={EDITOR_JS_TOOLS}
data={data}
onChange={() => handleSave}
/>
</div>
</div>
);
};
If I use
onChage={() => handleSave}
I get this error.
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'save')
Thanks for your help!
As per ReactEditorJS docs - you should use onInitialize to save your instance on initialize.
So in your case try this -
const Create = () => {
const instanceRef = React.useRef(null);
let data = { '1': 'test' }
async function handleSave() {
const savedData = await instanceRef.current.save()
console.log(savedData)
}
return (
<div>
<Header Name='Создание'/>
<Menu/>
<div className="editor">
<ReactEditorJS
//changed instanceRef to onInitialize
onInitialize={(instance) => (instanceRef.current = instance)}
tools={EDITOR_JS_TOOLS}
data={data}
//need to add '()' to call handleSave
// or you can change it to onChange={handleSave}
onChange={() => handleSave()}
/>
</div>
</div>
);
};
References -
How to access editor-js instance?
Note - I have not tried this solution but by reading the documentation I have added this answer, let me know if it does not work.

How can I map images in React from firebase storage?

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

Fetched Object from API doesn't appear in mapping

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 {}.

Categories