Reactjs not Rendering view define in foreach loop - reactjs

I am fetching data from firebase and set the data using useState, but when I am looping through in component return not viewing anything in the loop. Here is my code.
function App() {
const regRef = ref(database, "Tasks/");
const [tasks, setTasks] = useState([{ title: "Task List" }]);
useEffect(() => {
onValue(regRef, (snapshot) => {
snapshot.forEach((childSnapshot) => {
childSnapshot.forEach((task) => {
setTasks((oldTasks) => [...oldTasks, task.val()]);
});
});
});
}, []);
return (
<div className="App">
<div>Rendering data</div>
{tasks.forEach(function (task) {
console.log("rendering value: " + task.title);
return (
<>
<h2>{task.title}</h2>
<h4>{task.description}</h4>
</>
);
})}
</div>
);
}
In this view, there is only Rendering data div, but in console log I am getting all the value

Problem
forEach just iterate the array it doesn't return anything. That's why you can't see anything. Because it doesn't returning anything
Solve
Try this. Because map returns an array.
{tasks.map(function (task) {
console.log("rendering value: " + task.title);
return (
<>
<h2>{task.title}</h2>
<h4>{task.description}</h4>
</>
);
})}

Related

data.map is not a function react js

I'm new to react and trying to connect firestore for my project.
I followed the example from the Internet and everything works, the data is deleted and written to the database, also when I change the data they change in the database, but I get errors in the console and a white screen.
Uncaught TypeError: data.map is not a function
If you need any more files or code, I will correct my question, please write which ones I need to add
Also, when loading the page, I get the following error in the console:
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist. at wrappedSendMessageCallback
Here is the code that throws the error:
export default function Saved({ data, setData }) {
function editData(id, newWord, newTranslate, newNote) {
const editedDataList = async (card) => {
if (id === card.id) {
return {
...card,
word: newWord,
translate: newTranslate,
note: newNote,
};
}
let newFields = {
word: newWord,
translate: newTranslate,
note: newNote,
}
await updateDoc(doc(db, "db-name", id), newFields);
console.log(newFields)
return card;
};
setData(editedDataList);
}
const deletePost = async (id) => {
await deleteDoc(doc(db, "db-name", id));
};
const dataList = data.map((card) => (
<SavedData
id={card.id}
key={card.id}
word={card.word}
translate={card.translate}
note={card.note}
editData={editData}
del={deletePost}
/>
));
return (
<div>
<div className="sec-menu"></div>
<div className="saved-inner">
{data.length >= 1 ? (
<div className="saved-list">{dataList}</div>
) : (
<Link className="main-btn" to="/addcard">
Add
</Link>
)}
</div>
</div>
);
}
Here Menu.js code:
function Menu() {
const [data, setData] = useState([]);
useEffect(() => {
const q = query(collection(db, "db-name"));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
let wordsArr = [];
querySnapshot.forEach((doc) => {
wordsArr.push({ ...doc.data(), id: doc.id });
});
setData(wordsArr);
});
return () => unsubscribe();
}, []);
return (
<div className="content">
<AuthContextProvider>
<Routes>
<Route
path="saved"
element={<Saved data={data} setData={setData} />}
/>
</Route>
</Routes>
</AuthContextProvider>
</div>
);
}
export default Menu;
On second glance, the issue is where you call setData(editedDataList). You're passing in a function into this method which is in turn updating data to be a function instead of an array. Try changing, editData() to be something like this:
const editData = async (id, newWord, newTranslate, newNote) => {
const editedDataList = await Promise.all(data.map(async (card) => {
let newFields = {
word: newWord,
translate: newTranslate,
note: newNote,
};
if (id === card.id) {
return { ...card, ...newFields };
}
await updateDoc(doc(db, "db-name", id), newFields);
console.log(newFields);
return card;
}));
setData(editedDataList);
};
editedDataList will be an array of the modified cards in the original and setData() should work as expected.
maybe the error occurs because "data" object is not an array.
And check what are you setting on "setData(editedDataList);" instruction

Why my useState in useEffect is not rerendering?

I tried a lot of different solutions and none worked. The problem is that I'm calling a function inside useEffect, the function is working well, the variable is getting the result I want but when I'm going to set the state, nothing happens but the state update and I can see in react tools extension.
This is my code, I'm receiving as props cart that is a array of objects [{id, quantity}]
fetchDetail is a simple function that calls an api and It's working
I saw some answers saying that the useEffect does not recognize the update so I't don't rerender but I don't know why.
const [items, setItems] = React.useState([]);
async function mapIds() {
const result = [];
await Promise.all(
cart.map((product) => {
fetchDetail(product.id).then((data) =>
result.push({
id: data.id,
title: data.title,
quantity: product.quantity,
thumbnail: data.thumbnail,
price: data.price,
})
);
})
);
setItems(result);
}
React.useEffect(() => {
mapIds();
}, [cart]);
return (
<>
<section id="cart-container">
<h1>Carrinho de compras</h1>
{items.length > 0 ? (
items.map((item) => (
<div>
<p key={item.id}>{item.title}</p>
</div>
))
) : (
<p>Carrinho vazio</p>
)}
</section>
</>
);
}
The promises aren't being awaited because Promise.all isn't seeing them:
cart.map((product) => {
// This callback to .map() doesn't return anything
fetchDetail(product.id).then((data) =>
//...
)
});
The callback to .map() needs to return the Promise:
cart.map((product) => {
// return the Promise...
return fetchDetail(product.id).then((data) =>
//...
)
});

How can I convert HTMLCollection to Array instead of an empty array in React?

i'm studying react and also an beginner. ploblem had caused when i was trying to convert HTMLCollection into an array. here's code.
const HeroSlide = ({ items }) => {
const heroSlide = useRef(null);
useEffect(() => {
const arr = Array.from(heroSlide.current.children);
arr.map((child) => {
child.className = 'text only';
});
console.log(arr);
// got 4 text-only divs. divs created by using map func are excluded
}, []);
return (
<div className="heroSlide" ref={heroSlide}>
<div>text only</div>
{items.map((e, i) => (
<div className="heroSlide__items">
<img
src={apiConfig.originalImage(e.backdrop_path)}
className="heroSlide__backgroundImage"
alt=""
/>
</div>
))}
<div>text only</div>
<div>text only</div>
<div>text only</div>
</div>
);
};
if i use setTimeout console.log works. but i need better way.
useEffect(() => {
setTimeout(() => {
const arr = Array.from(heroSlide.current.children);
console.log(arr);
}, 50);
}, []);
create a variable and push values as shown below -
const heroSlide = useRef(null);
let arr = []
useEffect(() => {
console.log(heroSlide.current.children);
console.log('array is',arr.push(...heroSlide.current.children))
console.log('arr',arr)
}, []);
I solved problem in this way. I passed the dynamic process to the child node to perform it. now i gets complete array.
const HeroSlideItems = ({ items }) => {
return (
<div className="heroSlide__items">
{items.map((e, i) => {
return (
<div className="heroSlide__items__index">
<img
src={apiConfig.originalImage(e.backdrop_path)}
className="heroSlide__backgroundImage"
alt=""
/>
</div>
);
})}
</div>
);
};
const HeroSlide = ({ items }) => {
const heroSlideRef = useRef(null);
useEffect(() => {
if (heroSlideRef.current) {
const children = Array.from(heroSlideRef.current.children);
children.forEach((c) => {
c.className = 'changed';
});
}
}, []);
return (
<div className="heroSlide" ref={heroSlideRef}>
<div>testclass</div>
<HeroSlideItems items={items} />
<div>testclass</div>
<div>testclass</div>
<div>testclass</div>
</div>
);
};

How to wait for setState in useEffect until render?

let [item, setItem] = useState({});
let [comments, setComments] = useState([]);
useEffect(async () => {
await axios
.all([
axios.get(`https://dummyapi.io/data/v1/post/${id}`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
}),
axios.get(`https://dummyapi.io/data/v1/post/${id}/comment`, {
headers: { "app-id": process.env.REACT_APP_API_KEY }
})
])
.then(
axios.spread((detail, comment) => {
setItem({ ...detail.data })
setComments([...comment.data.data])
})
)
.catch((detail_err, comment_err) => {
console.error(detail_err);
console.error(comment_err);
});
}, []);
i setStated like above.
and I was trying to use the State in return(), but it seems it didn't wait for the data set.
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
because i got an error message like this : Uncaught TypeError: Cannot read properties of undefined (reading 'map').
Since i initialized 'item' just empty {object}, so it can't read 'item.tags', which is set by setState in useEffect.
How can i wait for the data set?
In generic, it would set a state isFetched to determine if the data from api is ready or not. And when the isFetched equal to true, it means the item.tags have value.
const [isFetched, setIsFetched] = useState(false);
useEffect(async () => {
await axios.all(...).then(() => {
...
...
setIsFetched(true);
})
}, [])
// You could return null or an Loader component meaning the api is not ready
if (!isFetched) return null;
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
On the other hand, you could use optional chaining to avoid using map from an undefined value (that is item.tags), the right way is replace item.tags.map to item.tags?.map.
Initially, item is an empty JSON ({}). You should be using the optional chaining operator(?.) to easily get rid of the null or undefined exceptions.
return (
<div>
{item?.tags?.map((tag, index) => {
return <Chip label={tag} key={index} />
})}
</div>
)
let [item, setItem] = useState({});
Your initial state is an empty object, and there will always be at least one render that uses this initial state. Your code thus needs to be able to work correctly when it has this state. For example, you could check if item.tags exists before you try to use it:
if (item.tags) {
return (
<div>
{item.tags.map((tag, index) => {
return <Chip label={tag} key={index] />
})}
</div>
);
} else {
return <div>Loading...</div>
}
Alternatively, you could change your initial state so it has the same shape that it will have once loading has finished:
let [item, setItem] = useState({ tags: [] });

array.forEach is not a function

I'm showing array of objects that is received from api call.I'm using react hooks.when I tried to iterate through array of objects it return foreach is not a function.
for this can I use await and async.
function searchArticle() {
const [articleList, setArticleList] = useState([]);
const { articleApiStatus, articleCurrentPage, searcArticle } = state;
useEffect(() => {
setArticleList(state.articleList);
articleListApiCall(articleCurrentPage);
}, [])
const articleListApiCall = (page = 1) => {
const isNewPage = articleCurrentPage !== page;
if (!articleApiStatus || isNewPage) {
getArticleList(page, '')
}
}
const getArticleList = async (page, searchkey) => {
const requestBody = {
page: page - 1, searchkey
}
await onArticleSearchPost(requestBody).then(articleresult => {
setArticleList(articleresult);
}).catch(err => {
console.log(err);
})
}
return (
<React.Fragment>
<div className="article-section">
<div className="search-result">
<Collapse >
{
articleList.forEach(element => {
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
})
}
</div>
<div className="pagination-section">
<Pagination defaultCurrent={1} total={50} />
</div>
</div>
</React.Fragment>
)
}
export default searchArticle;
Edit : I'm receiving following data from api call
[
{
"_id":"5d9efbc3b29b2876700adf6f",
"postedBy":"Admin",
"datePosted":"1570700227888",
"__v":0
}
]
First of all, do not use forEach to render JSX, it won't work. Use map instead:
<Collapse>
{articleList.map(element => { // use map
return (
<Panel header={element.article_title} key={element._id}>
<p>{element.article_body}</p>
</Panel>
);
})}
</Collapse>
Second, make sure that state.articleList, which you're setting to state in useEffect, is an actual array.

Resources