Here I've fetched workouts from backend through api. It shows output in the console but unable to map through workouts state in the webpage.
import React, { useEffect, useState } from "react";
const Home = () => {
const [workouts, setWorkouts] = useState([]);
useEffect(() => {
const fetchWorkouts = async () => {
const response = await fetch("http://localhost:4000/api/workouts");
const json = await response.json();
if (response.ok) {
console.log('success');
console.log(json);
setWorkouts(json);
}
};
fetchWorkouts();
}, []);
return (
<div className="home">
<div className="workouts">
{workouts &&
workouts.map((workout) => {
<p key={workout._id}>{workout.title}</p>;
})}
</div>
</div>
);
};
export default Home;
You forgot to return it. Do this:
return <p key={workout._id}>{workout.title}</p>;
or you can also do this:
{workouts?.map((workout) => (
<p key={workout._id}>{workout.title}</p>
))}
You can remove the bracket on the map:
workouts.map((workout) =>
<p key={workout._id}>{workout.title}</p>;
)}
You're not returning anything. Either explicitly use the return keyword to return the element or You can do this in a more appropriate way like this.
{
workouts &&
workouts.map((workout) => (
<p key={workout._id}>{workout.title}</p>
))
}
i'm currently creating my first MERN App, and everything is going well, until something happened, and i'm going my try to explain because i need help !
What i'm doing is a facebook clone, where you can post something, you can delete your post and you can update your post, the logic is simple, i call dispatch to pass the data to the actions, the actions pass the data to the backend, and the backend return something to me and it saves in my store, because i'm using redux
The problem is that, when i have 2 post, and i want to delete a post, or maybe i want to edit it, the other post dissapears, it's like it loses its id and then loses the information, then i can't do anything but reaload the page, and it happens always
this is how it looks like, everything fine
Then, after trying to edit a post, the second one lost its information, and in the console, it says that Warning: Each child in a list should have a unique "key" prop, and i already gave each post the key={_id}, but the post lost it and i don't know how
Here's the code
Posts.js
import React, { useState } from "react";
import "./Posts.css";
import moment from "moment";
// Icons
import { BiDotsVertical, BiLike } from "react-icons/bi";
import { MdDeleteSweep } from "react-icons/md";
import { AiFillLike } from "react-icons/ai";
import { GrClose } from "react-icons/gr";
// Calling actions
import { deletePost, } from "../actions/posts.js";
// Gettin The Data From Redux
import { useSelector, useDispatch } from "react-redux";
const Posts = ({ setCurrentId }) => {
const [animation, setAnimation] = useState(false);
const [modal, setModal] = useState(false);
const [modalPost, setModalPost] = useState({});
// Getting The Posts
const posts = useSelector(state => state.posts);
const dispatch = useDispatch();
// Showing And Hiding Modal Window
const ModalWindow = post => {
setModalPost(post);
setModal(true);
};
// Liking the post
// const Like = id => {
// dispatch(giveLike(id));
// setAnimation(!animation);
// };
if (!posts.length) {
return <div>Loading</div>;
} else {
return (
<div className="Posts">
{/* // Modal window for better look to the post */}
{/* {modal && (
<div className="modalWindow">
<div className="container">
<div className="container-image">
<img src={modalPost.image} alt="" />
</div>
<div className="information">
<div className="container-information">
<div className="data-header">
<h2>
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(modalPost.createdAt).fromNow()}
</span>
</h2>
<span className="data-icon" onClick={() => setModal(false)}>
<GrClose />
</span>
</div>
<div className="message">
<h2>{modalPost.title}</h2>
<p>{modalPost.message}</p>
</div>
</div>
</div>
</div>
</div>
)} */}
{/* */}
{posts.map(post => {
const { _id, title, message, image, createdAt, likes } = post;
return (
<div className="Posts-container" key={_id}>
<div className="Fit">
<div className="Fit-stuff">
<h2 className="Fit-stuff_title">
User <br />{" "}
<span style={{ fontWeight: "400" }}>
{moment(createdAt).fromNow()}
</span>
</h2>
<a
className="Fit-stuff_edit"
href="#form"
onClick={() => setCurrentId(_id)}
>
<BiDotsVertical />
</a>
</div>
<div className="Fit-data">
<h2 className="Fit-data_title">{title}</h2>
<p className="Fit-data_message">{message}</p>
{image ? (
<div className="Fit-img">
<img
onClick={() => ModalWindow(post)}
src={image}
alt=""
/>
</div>
) : (
<div></div>
)}
</div>
<div className="Fit-shit">
<span>
{animation ? (
<AiFillLike className="fullLightBlue" />
) : (
<BiLike />
)}
{likes}
</span>
<span onClick={() => dispatch(deletePost(_id))}>
<MdDeleteSweep />
</span>
</div>
</div>
</div>
);
})}
</div>
);
}
};
export default Posts;
The form where i call update and create Post
import React, { useState, useEffect } from "react";
import Filebase from "react-file-base64";
// For the actions
import { useDispatch, useSelector } from "react-redux";
import { createPost, updatePost } from "../actions/posts.js";
import {
Wrapper,
FormContainer,
Data,
DataInput,
SecondDataInput,
FormContainerImg,
FormContainerButtons,
Buttons
} from "./FormStyled.js";
const Form = ({ currentId, setCurrentId }) => {
const [formData, setFormData] = useState({
title: "",
message: "",
image: ""
});
const specificPost = useSelector(state =>
currentId ? state.posts.find(p => p._id === currentId) : null
);
// Sending The Data And Editing The data
const dispatch = useDispatch();
useEffect(() => {
if (specificPost) setFormData(specificPost);
}, [specificPost]);
// Clear Inputs
const clear = () => {
setCurrentId(0);
setFormData({ title: "", message: "", image: "" });
};
const handleSubmit = async e => {
e.preventDefault();
if (currentId === 0) {
dispatch(createPost(formData));
clear();
} else {
dispatch(updatePost(currentId, formData));
clear();
}
};
return (
<Wrapper>
<FormContainer onSubmit={handleSubmit}>
<Data>
<DataInput
name="title"
maxLength="50"
placeholder="Title"
type="text"
value={formData.title}
onChange={e => setFormData({ ...formData, title: e.target.value })}
/>
<SecondDataInput
name="message"
placeholder="Message"
maxLength="300"
value={formData.message}
required
onChange={e =>
setFormData({ ...formData, message: e.target.value })
}
/>
<FormContainerImg>
<Filebase
required
type="file"
multiple={false}
onDone={({ base64 }) =>
setFormData({ ...formData, image: base64 })
}
/>
</FormContainerImg>
<FormContainerButtons>
<Buttons type="submit" create>
{specificPost ? "Edit" : "Create"}
</Buttons>
<Buttons onClick={clear} clear>
Clear
</Buttons>
</FormContainerButtons>
</Data>
</FormContainer>
</Wrapper>
);
};
export default Form;
My actions
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
import * as api from "../api/posts.js";
export const getPosts = () => async dispatch => {
try {
const { data } = await api.getPosts();
dispatch({ type: GETPOSTS, payload: data });
} catch (error) {
console.log(error);
}
};
export const createPost = newPost => async dispatch => {
try {
const { data } = await api.createPost(newPost);
dispatch({ type: CREATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const updatePost = (id, updatePost) => async dispatch => {
try {
const { data } = await api.updatePost(id, updatePost);
dispatch({ type: UPDATEPOST, payload: data });
} catch (error) {
console.log(error);
}
};
export const deletePost = id => async dispatch => {
try {
await api.deletePost(id);
dispatch({ type: DELETEPOST, payload: id });
} catch (error) {
console.log(error);
}
};
Redux Part
import {
GETPOSTS,
CREATEPOST,
DELETEPOST,
UPDATEPOST,
LIKEPOST
} from "../actionTypes/posts.js";
const postData = (posts = [], action) => {
switch (action.type) {
case GETPOSTS:
return action.payload;
case CREATEPOST:
return [...posts, action.payload];
case UPDATEPOST:
return posts.map(post =>
action.payload._id === post._id ? action.payload : posts
);
case DELETEPOST:
return posts.filter(post => post._id !== action.payload);
default:
return posts;
}
};
export default postData;
My controllers in the backend
import mongoose from "mongoose";
import infoPost from "../models/posts.js";
// Getting All The Posts
export const getPosts = async (req, res) => {
try {
const Posts = await infoPost.find();
res.status(200).json(Posts);
} catch (error) {
res.status(404).json({ message: error.message });
console.log(error);
}
};
// Creating A Post
export const createPost = async (req, res) => {
const { title, message, image } = req.body;
const newPost = new infoPost({ title, message, image });
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message: error.message });
console.log(error);
}
};
// Update A Post
export const updatePost = async (req, res) => {
const { id } = req.params;
const { title, message, image } = req.body;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No Post With Id Of ${id}`);
const updatedPost = { title, message, image, _id: id };
await infoPost.findByIdAndUpdate(id, updatedPost, { new: true });
res.json(updatedPost);
};
// Deleting A Post
export const deletePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res
.status(404)
.send(`We Couldnt Found The Post With Id Of ${id} To Delete`);
await infoPost.findByIdAndRemove(id);
res.json(`Post With Id Of ${id} Deleted Succesfully`);
};
// Liking A Post
export const likePost = async (req, res) => {
const { id } = req.params;
if (!mongoose.Types.ObjectId.isValid(id))
return res.status(404).send(`No post with id: ${id}`);
const post = await infoPost.findById(id);
const updatedPost = await infoPost.findByIdAndUpdate(
id,
{ likeCount: post.likeCount + 1 },
{ new: true }
);
res.json(updatedPost);
};
Even though i've been trying to solve this problem for nearly 3.5 hours, i think that the problem might be in my Posts.js part, if you can help me, you're the greatest !
I have a file called index.js it contain the query to select data from my table.
In another file I have written code to bring data from my table. When I execute the code I can print some words but I can't print or bring data from the table.
class MyApp extends React.Component{
constructor(props){
super(props)
this.state= {
employees:[],
success : 0
}
this.getList = this.getList.bind(this)
}
getList(){
let config = {
url:'/api/gettable '
};
return new Promise((resolve,reject) =>{
customFetch(config)
.then((res) => {
resolve(res.rows);
}).catch((err) => {
reject(new Error(err));
});
})
}
async componentDidMount(){
try{
let employeeData = await this.getList();
this.setState({
employees:employeeData
})
}
catch(err){
console.log(err);
}
}
render(){
return(
<div className="dashboard animated slideInUpTiny animation-duration-3 ">
<div className="row">
<div className="app-wrapper pb-0">
<div>
<h1>Welcom</h1>
{this.state.employees.map(employee => <div>Id={employee.Id} Name={employee.Name} Description={employee.Description} Date={employee.Date} </div>)}
</div>
</div>
</div>
</div>
);
}
}
export default MyApp ;
It prints welcome but didn't print the data of table.
Try this :
Change this line
this.state.employees.map(employee => <div>Id={employee.Id} Name={employee.Name} Description={employee.Description} Date={employee.Date} </div>)}
To
{this.state.employees && this.state.employees.map(employee => <div>Id={employee.Id} Name={employee.Name} Description={employee.Description} Date={employee.Date} </div>)}
I'm testing a react component UI. Within this component, a request is sent and fetch data to rerender UI. Now a snapshot before the request fetch data is produced. How to get a snapshot after the request?
// component.js
class Text extends Component {
componentDidMount() {
this.load()
}
load = () => {
const {id} = this.props
fetch('/abc').then(data => {
this.setState({data})
})
}
render() {
if(!this.state.data) return null
const {data} = this.state
return (
<div>
{data}
</div>
)
}
}
//jest
describe('test', () => {
beforeEach(() => {
fetch.mockImplementation(()=> new Promise(resolve=>resolve(4)))
});
test('base render', async () => {
const wrapper = await render(<Text/>)
expect(toJson(wrapper)).toMatchSnapshot()
})
})
//received snapshot
null
//expected snapshot
<div>
4
</div>
I'm using ReactJS, NodeJS, MongoDB.
In my project I have a Task List and I'm adding new tasks (this works!) but only appends/show that new task when I refresh the page but I'm using ReactJS so I can have a more responsive/interactive website but I'm new at this and I'm still learning and I don't know what to do...Maybe I have to make something with the state?!
Hope you can help me! Thanks!
Here's my NewTask Component:
import React, { Component } from 'react';
import './NewTask.css';
class NewTask extends Component {
constructor(props) {
super(props);
this.state = {
projectId: null,
tasktitle: '',
taskcomment: ''
};
}
postDataHandler = () => {
let data = {
tasktitle: this.state.tasktitle,
taskcomment: this.state.taskcomment
};
fetch(`/dashboard/project/${this.props.projectId}/tasks/newtask`, {
method: 'POST',
data: data,
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json'
}
}).then(response => { return response.json() })
.catch(error => console.error('Error:', error));
}
render() {
return (
<div>
<input type='text' className='form-control input--task' required placeholder='Task Title' value={this.state.tasktitle} name='tasktitle' ref='tasktitle' onChange={(event) => this.setState({ tasktitle: event.target.value })} />
<button type='submit' className='btn btn-default button--newtask' value='Submit' onClick={this.postDataHandler}>Add Task</button>
</div>
);
}
}
export default NewTask;
Here's server side to create new task
//Create New Task
exports.create_new_task = (req, res) => {
let projectid = req.params.id;
Task.create({
tasktitle: req.body.tasktitle,
taskcomment: req.body.taskcomment,
project: req.params.id
}, (err, tasks) => {
if (err) {
console.log(err);
}
Project.findById(projectid, (err, project) => {
if(err) {
console.log(err);
}
project.tasks.push(tasks._id);
project.save();
console.log('NEW Task added to project: ' + projectid)
res.json(tasks)
});
});
};
Here's my Tasks Component
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt, faEdit } from '#fortawesome/free-solid-svg-icons'
import './Tasks.css';
class Tasks extends Component {
constructor(props) {
super(props);
this.state = {
projectId: props._id,
tasks: []
};
}
componentDidMount() {
fetch(`/dashboard/project/${this.props.projectId}/tasks`)
.then(response => {
return response.json()
}).then(task => {
this.setState({
tasks: task.tasks
})
}).catch(error => console.error('Error:', error));
}
render() {
const fontawesomeiconStyle = {
fontSize: '1em',
color: '#8e8359',
textAlign: 'center'
}
const listStyle = {
display:'grid',
gridTemplateColumns:'2fr 1fr',
alignItems: 'center',
justifyItems: 'center'
}
const { tasks } = this.state;
return (
<div>
<ul className="task-list">
{tasks.map(task =>
<li key={task._id} style={listStyle}>{task.tasktitle}
<div>
<form method='POST' action={`/dashboard/project/${this.props.projectId}/tasks/delete?_method=DELETE&taskId=${task._id}`}>
<button className="button--tasks" >
<FontAwesomeIcon style={fontawesomeiconStyle} icon={faTrashAlt} />
</button>
</form>
</div>
</li>
)}
</ul>
</div>
);
}
}
export default Tasks;
Here's a gif so you can see what's really happening, only appends the
new task when I refresh the page..
You can return a task object from your POST method and then append to the existing task list. Something like this:
postDataHandler = () => {
/* removed for brevity */
.then(response => response.json())
.then(response => {
// append to existing list of tasks
this.props.appendTask(response);
})
.catch(error => console.error('Error:', error));
}
// method in parent component
// passed down through props
appendTask = task => {
let tasks = [...this.state.tasks];
tasks.push(task);
this.setState({tasks});
}
Your list will only re-render when a change in state affects what's being rendered. You either need to re-fetch the full list of tasks or manually append your new task, which is what's being done in the above example.
Here is a more complete example:
class TaskList extends Component {
constructor(props) {
super(props);
this.state = {
tasks: [
{/* task 1 */},
{/* task 2 */}
]
}
}
appendTask = task => {
let tasks = [...this.state.tasks];
tasks.push(task);
this.setState({tasks});
}
render() {
const { tasks } = this.state;
return (
<div className="tasks">
<ul>
{tasks.map(task => <TaskItem task={task}/>)}
</ul>
<NewTask appendTask={this.appendTask}/>
</div>
);
}
}
class NewTask extends Component {
/* ... */
postDataHandler = () => {
/* ... */
.then(response => response.json())
.then(response => {
// append to existing list of tasks
this.props.appendTask(response);
})
.catch(error => console.error('Error:', error));
}
/* ... */
}
After you POST the new item your have to GET the new item as well in your item list component.
You could put both the NewTask and TaskList components in one class component that could perform a GET after the POST promise resolves and update the state with the new item.
Or you could use Redux or another state handler that would use actions that trigger things in the right order.
Look, you're making a POST request to the backend, right?
As it seems, it gets stored correctly, but you're NOT doing anything with it. One way is to do it in a similar fashion as #wdm suggested, or just append the 'task' to your current state using setState, but only if it was posted in the first place, right? :)
Make sure that the response from the backend is the data you posted, use that response and append it to the already existing state using the ... spread operator. The setState will trigger a re-render and you'll have all your tasks listed.