TypeError: users.map is not a function React js - node.js

am stuck with a problem , am try to solve it lots of time but am not able to solve it , please try to fix my error. If you have any query please free feel to ask.
Userdata.js
This is the userdata.js file where I want to load my all data which are in backend database
import React, { useEffect, useState } from "react";
import { Link,useParams } from "react-router-dom";
import Axios from 'axios';
const UserData = () => {
const [users, setUser] = useState({
title : "",
description : ""
});
const {id} = useParams();
useEffect(() => {
AllUsers();
}, []);
const AllUsers = async () => {
const res = await Axios.get(`http://localhost:3000/${id}`);
console.log(res.data);
setUser(res.data)
};
return (
<div>
<div className="container">
<table className="table table-hover table-bordered mt-5">
<thead>
<tr>
{/* <th scope="col">No</th> */}
<th scope="col">Title</th>
<th scope="col">Details</th>
<th scope="col">Action</th>
</tr>
</thead>
<tbody>
{users.map((user, index) => (
<tr key={index}>
<th scope="row">{user.id}</th>
<td>{user.title}</td>
<td>{user.description}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default UserData;

users looks to me to be an object, not an array. The map() function exists only on Array's prototype, so you can't call it on your users object. Did you mean to initialize users like this?
const [users, setUser] = useState([{
title : "",
description : ""
}]);

The map method is defined on on array and not on objects. Lets look at your code
const [users, setUser] = useState({
title : "",
description : ""
});
In UserData component you defined an object state with properties title and description. So users will be an object with those properties. Thus when you try to apply map on the users object it fails since map is not a function defined on objects.
Instead if you want to have an array of users with those two properties you can declare the state as follows
const [users, setUser] = useState([{
title : "",
description : ""
}]);

Related

Show detail Button in table not working(instead pages gets reload) and Not getting all the data properly of the table from mongodb

As I click on the Show detail button I should get an alert/more info of the user but instead of that the page gets reload. Basically on 'show details' button the 'OnClick' function is not getting executed.
Why am I getting no enteries found after clicking on 'show details'
Fetching data from Database and setting it in 'clientTable' variable:
React.useEffect(()=>{
window.onload=()=>{
const datatablesSimple = document.getElementById('datatablesSimple');
if (datatablesSimple) {
new DataTable(datatablesSimple);
}
const fetchClients = async ()=>{
const res = await axios.get("/users")
setClientTable((clientTable)=>{return clientTable=res.data;})
console.log(clientTable)
}
fetchClients()
}
},[]);
Nodejs code for fetching all the Clients
router.get("/", async(req, res) => {
try{
const allclients = await User.find();
res.status(200).json(allclients);
}catch(err){
res.status(500).json(err);
}
});
HTML code of rendering the table in frontend:
<tbody>
{clientTable.map((User, index)=>(<>
<tr key={index}>
<td>
<Link to="/details" state={User}
style={{textDecoration:'none',
color:'black'}} > {User.username}
</Link>
</td>
<td>{User.email}</td>
<td>{User.dob}</td>
<td>{User.city}</td>
<td>{User.services}</td>
<td><button onClick={handleRowClick}> show details</button></td>
</tr>
</>
))}
</tbody>
handleRowClick method implementation:
const handleRowClick = (e) => {
e.preventDefault();
alert("Hello")
//setShowPopup(true);
}

React JS Modal Show White Blank Screen

I have a data called person in JSON format getting from API in the file User.js:
const [person, setPerson] = useState([]);
const url = "http://localhost:8080/api/persons";
useEffect(() => {
axios.get(url).then((response) => {
setPerson(response.data);
});
}, [url]);
In another file called UpdatePersonForm.js I'm trying to show that data in popup windows after clicking a button.
export const UpdatePersonForm= ({ person, personEditOnSubmit }) => {
return (
<div>
{person.map((item) => (
<tr>
<td>{item.name}</td>
</tr>
))}
</div>
}
then it shows a white blank screen again. If I called an API directly from UpdatePersonForm.js then it works fine. For example:
export const UpdatePersonForm= ({ personEditOnSubmit }) => {
const [person, setPerson] = useState([]);
const url = "http://localhost:8080/api/persons";
useEffect(() => {
axios.get(url).then((response) => {
setPerson(response.data);
});
}, [url]);
return (
<div>
{person.map((item) => (
<tr>
<td>{item.name}</td>
</tr>
))}
</div>
}
However, if I get data from the parent file like the above then I got wrong.
You initialize the person variable with const [person, setPerson] = useState(""); which means that on first render person will be a string and strings do not have a .map method.
Use const [person, setPerson] = useState([]); and you should be fine.
Since you expect it to be an array after the JSON is fetched, you should also initialize it to one.

Node.js Cheerio returning empty with no errors

i am trying to get data from table with structure like this:
<table id="ros_table" class="info" style="display: none;">
<tr>
<th>First</th>
<th>Second</th>
<th>Third</th>
<th>Forth</th>
<th>Fifth</th>
<th>Six</th>
</tr>
<tr>
<td style="white-space: nowrap;">120241</td>
<td style="white-space: nowrap;">69801:001:0255</td>
<td>Name</td>
<td>Name 2</td>
<td><span style="white-space: nowrap;">90400 m<sup>2</sup></span> <span style="white-space: nowrap;">(9.04 ha)</span></td>
<td style="white-space: nowrap;">jah</td>
</tr>
And the code im useing is this:
fetchData(url).then( (res) => {
const html = res.data;
const $ = cheerio.load(html);
const statsTable = $('.table#ros_table > tr');
statsTable.each(function() {
let title = $(this).find('td').text();
console.log(title);
});
})
async function fetchData(url){
console.log("Looking for stuff you need...")
// Make the call
let response = await axios(url).catch((err) => console.log(err));
if(response.status !== 200){
console.log("Blah, this did not work out");
return;
}
return response;
}
It works fine with just a simple que, but for some reason i can get it to work for this table.
You need to return something out of each .then() block, i assume you want something like this?:
fetchData(url).then( (res) => {
const html = res.data;
const $ = cheerio.load(html);
const statsTable = $('.table#ros_table > tr');
return statsTable.map(function() {
return $(this).find('td').text();
});
})

Show added posts without refreshing page in React

I have been working on a personal project outside university, developing a blog.
Right now I'm trying to implement a "home page" where after a succesfull login, the user can post text, and right after that it appears under the Create post div you can see in the pic
This is what I have managed to accomplish so far:
This is the home page after login
Right now I can login, and post a new post which saves it in the database.
This is the home.js functional componenet which the user sees after a login:
import '../App.css';
import { useHistory } from "react-router-dom";
import React , {useState, useEffect} from 'react';
import jwt_decode from 'jwt-decode'
import logo from '../images/home-logo.png';
import {Col,Form,Input,Button,Card,CardTitle,Navbar,Nav,NavbarBrand} from 'reactstrap'
import { createPost,getUserPosts } from '../fucntions/user_functions'
function Home(){
var _decoded;
var _email;
let history = useHistory();
const[post_text,setPost] = useState('');
const handleChangePost = e =>{ setPost(e.target.value);};
function handlePost(e){
e.preventDefault();
const toPost = {
post :post_text, email :_email
}
createPost(toPost).then(res =>{
setPost('')
})
}
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
})
}
function handleLogout (e) {
e.preventDefault();
localStorage.removeItem('usertoken')
history.push(`/login`)
}
useEffect(() =>{
if (localStorage.getItem("usertoken") === null) {
history.push('/login')
} else {
const token = localStorage.usertoken
const user_email = localStorage.useremail
const decoded = jwt_decode(token)
_decoded = decoded;
_email = decoded.email
getPosts()
};
});
return (
<div className = "box">
<div>
<Navbar color="light" light expand="md">
<Nav>
<NavbarBrand type = "button" onClick = {handleLogout}>Logout</NavbarBrand>
</Nav>
</Navbar>
<div className = "wrapper">
<Card body outline color="secondary" className = "card-home " >
<CardTitle><img src={logo} alt="logo"></img>Create post</CardTitle>
<Form onSubmit = {handlePost}>
<Input id = "tx" name = "input1" type = "textarea" value = {post_text} placeholder="Enter your post here" onChange= {handleChangePost}></Input>
<br></br>
<Col sm={{ span: 10, offset: 5 }}>
<Button outline color="primary" type="submit">Post!</Button>
</Col>
</Form>
</Card>
</div>
</div>
</div>
)
}
export default Home;
I have implemented a getPosts method in the backend which gives back an array of the posts
router.post("/getPosts",
async (req, res) => {
const {email,} = req.body;
try {
let user = await User.findOne({email:email});
allPosts = user.posts
res.render('/home',{posts : hello})
} catch (e) {
console.error(e);
res.json("Error")
}
}
);
As you can see above, in the function getPosts(), the response is an Array of all the post's ids the user has posted, they are stored in the mongodb collection called "posts"
And after calling that function, I can iterate over them:
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
forEach(res.posts) {
}
})
}
I want to render all those posts live, so each time the user posts a new post, it will show right after the Create post div you can see in the picture, What's the best way?
Thanks
First define your posts collection state:
const [allPosts, setAllPosts] = useState([]);
Then every time you successfully save a post in the database, append it to that state:
function handlePost(e){
e.preventDefault();
const toPost = {
post :post_text, email :_email
}
createPost(toPost).then(res =>{
setPost('')
setAllPosts(allPosts.concat(toPost);
})
}
The same goes for getPosts:
function getPosts() {
const container ={
email:_email
}
getUserPosts(container).then(res=>{
setAllPosts(res.data); // <-- if the data is the same structure as the created before
})
}
Then you can render them in an example way:
return (
<div className = "box">
<div>
<Navbar color="light" light expand="md">
<Nav>
<NavbarBrand type = "button" onClick = {handleLogout}>Logout</NavbarBrand>
</Nav>
</Navbar>
<div className = "wrapper">
<Card body outline color="secondary" className = "card-home " >
<CardTitle><img src={logo} alt="logo"></img>Create post</CardTitle>
<Form onSubmit = {handlePost}>
<Input id = "tx" name = "input1" type = "textarea" value = {post_text} placeholder="Enter your post here" onChange= {handleChangePost}></Input>
<br></br>
<Col sm={{ span: 10, offset: 5 }}>
<Button outline color="primary" type="submit">Post!</Button>
</Col>
</Form>
<div>
{
allPosts.map(post => {
return <div><div>email: {post.email}</div><div>post: post.post</div></div>
})
}
</div>
</Card>
</div>
</div>
</div>
)
Feel free to change the HTML structure, so it matches your design

How to display mongoDB collection in html?

I am a beginner with mongoose and would like to display a mongoDB document(s) from "exColl" collection in a file called "example.ejs" in a basic html list however I have hit various problems. There are other posts on this topic yet I remain stumped by this.
-I do have a working chunk of code that outputs all documents from exColl.find({}) using res.json, obviously putting them in json format. However I have been unable to adapt this code into something that works using res.render for example.
-When I define a variable in app.js and try to access it in example.ejs the variable is not found, therefore even if I could save the results of exColl.find({}) in a variable I don't see how I would be able to enter it into the HTML
Clearly I don't know what I don't know which is very frustrating. If someone could help fill my conceptual gaps that would be fantastic.
---Edit----
Adding a snippet I have tried
app.get("/example", function (req, res){
exColl.find({})
.exec(function (err, examples){
if (err) {
res.send("an error has occurred")
} else res.render(examples: examples);
});
});
In .ejs file
<p> <%= examples %> </p>
Your problem seems to be the EJS syntax which you should review here: EJS Docs. Consider the following test project structure:
.
├── index.js
├── package.json
├── setup.js
└── views
├── index.ejs
└── table.ejs
I create a test DB with setup.js so that we have some dummy posts to display:
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:8081/test", {
useNewUrlParser: true
});
const Post = mongoose.model("Post", {
title:String,
body: String
});
const toMake = [
{title: "hello", body: "world"},
{title: "foo", body: "bar"},
{title: "fizz", body: "buzz"},
{title: "a", body: "b"}
];
Post.insertMany(toMake)
.then(()=>{
console.log("done");
mongoose.connection.close();
})
.catch(err => console.error(err));
I create an EJS template views/table.ejs to render my posts as a table:
<table>
<thead>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<% posts.forEach(post => { %>
<tr>
<td><%= post.title %></td>
<td><%= post.body %></td>
</tr>
<% }) %>
</tbody>
</table>
I then create an EJS template views/index.ejs to use the table template
<main>
<h1>Posts</h1>
<%- include("table", {posts}); %>
</main>
I also make a server to respond to requests in index.js and run it with node index.js:
const express = require("express");
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:8081/test", {
useNewUrlParser: true
});
const app = express();
const Post = mongoose.model("Post", {
title: String,
body: String
});
app.set("view engine", "ejs");
app.get("/", async (req, res) => {
const posts = await Post.find({});
res.render("index", {posts});
});
app.listen(3000, () => console.log("Listening"));
And when I curl localhost:3000 I get the rendered HTML:
<main>
<h1>Posts</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>hello</td>
<td>world</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
<tr>
<td>fizz</td>
<td>buzz</td>
</tr>
<tr>
<td>a</td>
<td>b</td>
</tr>
</tbody>
</table>
</main>
No matter what, I will need to feed data to the res.render() function and populate the render scope with all the data needed to render.
However, I have made table.ejs reusable. So lets say that I have another page that I want to be able to show some of the posts in a tabular fashion.
I have another EJS template: views/profile.ejs that looks like this:
<main>
<h1>2 Posts</h1>
<%- include("table", {posts: posts.slice(0, 2)}); %>
</main>
And I add another route to my application at /sliced:
app.get("/sliced", async (req, res) => {
const posts = await Post.find({});
res.render("profile", {posts});
});
Whenever I curl localhost:3000/sliced I get only the first 2 items in the posts since I only populated the include's scope with a slice of all the posts:
<main>
<h1>2 Posts</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>hello</td>
<td>world</td>
</tr>
<tr>
<td>foo</td>
<td>bar</td>
</tr>
</tbody>
</table>
</main>

Resources