I have a social media app that I would like to function like facebook, where you comment and the comment is loaded in real-time with out having to refresh your browser to display the comment. I am able to send data from React to backend server and I am able to get that data with a axios http request, but I have to refresh the browser to see the comment displayed. I am also see the comment display more then once. I am not getting any errors but the comment is not unique to the post, as it is an array that loads the posts. Did I make a mistake in my code?
Here is the front end code.
import React, { useState, useEffect } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import axios from "axios";
import "./css/sharewall.css";
const ComponentName = () => {
const [posts, setPosts] = useState([]);
const [comment, setComment] = useState("");
const [id, setId] = useState("");
const loadData = async () => {
try {
let res = await axios.get(`http://localhost:5000/getall`);
setPosts(res.data);
} catch (error) {
console.log(error);
}
};
function makeRequest(e) {
e.preventDefault();
axios({
method: "POST",
url: "http://localhost:5000/postinput",
data: {
comment: comment,
},
}).then((res) => {
setComment(res.data.comment);
console.log(res.data);
});
}
const loadComment = async () => {
try {
let res = await axios.post("http://localhost:5000/postinput");
setComment(res.data.comment._id);
console.log(res.data.comment._id)
} catch (error) {
console.log(error);
}
};
useEffect(() => {
loadData();
}, []);
return (
<div className="compoentclass">
<Container className="mt-5 ml-auto mr-auto">
<div className="text-center">
{posts.map((post, index) => (
<div>
<Card className="">
<Card.Img alt="" src={post.url} />
<Card.ImgOverlay className="overlay">
<Card.Title className="text-center mt-5">
<Card.Text className="cardStyle text-light">
{post.body}
</Card.Text>
</Card.Title>
</Card.ImgOverlay>
</Card>
{posts.map((post, index) => (
<div><Card.Text>{post.comment}</Card.Text></div>
))}
<textarea
className="comment text-center mt-3 mb-3"
onChange={e => setComment(e.target.value)}
value={comment}
name={"comment"}
type={"text"}
/>
<div className="d-flex justify-content-start mt-n3 mb-4">
<Button
className="shareButton"
variant="secondary"
onClick={makeRequest}
onChange={loadComment}
>
Comment
</Button>
</div>
</div>
))}
</div>
</Container>
</div>
);
};
export default ComponentName;
Here is the render from the comments, the comments double or tripple.
In order for other users (the user that posts a comment should be easily able to see the comment immediately) to see the comments real-time, you must implement some sort of "listener" to the server/database to listen for new comments. Otherwise, how should my browser know that YOU posted a comment just now? Check out socket.io, it is quite easy to implement.
I've added some additions to your code, see comments.
First, it seems you can use useEffect to rerender your comments every time you will click the "comment" button. To handle updates you can create a new state as I did.
Probably you are having troubles with multi comments because your posts array contains more than one element inside. Inside render it maps through all posts array and displays every element.
Also, would be better if you will recreate your code inside codesandbox.io or similar.
import React, { useState, useEffect } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import axios from "axios";
import "./css/sharewall.css";
const ComponentName = () => {
const [posts, setPosts] = useState([]);
const [comment, setComment] = useState("");
const [id, setId] = useState("");
//state for resending load request
const [isCommentFetched, setCommentFetched] = useState(false);
const loadData = async () => {
try {
let res = await axios.get(`http://localhost:5000/getall`);
setPosts(res.data);
} catch (error) {
console.log(error);
}
};
function makeRequest(e) {
e.preventDefault();
axios({
method: "POST",
url: "http://localhost:5000/postinput",
data: {
comment: comment,
},
}).then((res) => {
setComment(res.data.comment);
setCommentFetched(true)
console.log(res.data);
})
//don't forget to catch errors
.catch((err)=>{
console.log(err)
})
}
const loadComment = async () => {
try {
let res = await axios.post("http://localhost:5000/postinput");
setComment(res.data.comment._id);
console.log(res.data.comment._id);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
loadData();
}, []);
//hook fires whenever your isCommentFetched state updating.
useEffect(() => {
// if isCommentFetched true, it will send request for get new posts, and will update your comments in render.
if(isCommentFetched){
loadData();
}
}, [isCommentFetched]);
return (
<div className="compoentclass">
<Container className="mt-5 ml-auto mr-auto">
<div className="text-center">
{posts.map((post, index) => (
<div>
<Card className="">
<Card.Img alt="" src={post.url} />
<Card.ImgOverlay className="overlay">
<Card.Title className="text-center mt-5">
<Card.Text className="cardStyle text-light">
{post.body}
</Card.Text>
</Card.Title>
</Card.ImgOverlay>
</Card>
{posts.map((post, index) => (
<div>
<Card.Text>{post.comment}</Card.Text>
</div>
))}
<textarea
className="comment text-center mt-3 mb-3"
onChange={(e) => setComment(e.target.value)}
value={comment}
name={"comment"}
type={"text"}
/>
<div className="d-flex justify-content-start mt-n3 mb-4">
<Button
className="shareButton"
variant="secondary"
onClick={makeRequest}
onChange={loadComment}
>
Comment
</Button>
</div>
</div>
))}
</div>
</Container>
</div>
);
};
export default ComponentName;
Related
im trying to build a weather app and i have been defined buttons to change the city name state and fetch the new city weather info by changing the dependency list of the useEffect hook and console log it for checking frst. but the problem is that the state is updating two steps behind the buttons that i click.
Main component for states and fetching data:
function App() {
const [cityName, setCityName] = useState('Toronto')
const [weather, setWeather] = useState('')
const url = `https://api.openweathermap.org/data/2.5/weather?q=${cityName}&units=metric&appid=5476766e86450fff39da1502218a3376`
const getData = () => {
axios.get(url).then((res) => {
setWeather(res.data);
});
console.log(weather)
}
useEffect(()=>{
getData()
},[])
return <div className="bg-slate-600">
<Topbuttons setCityName={setCityName} getData={getData} />
<Inputs setCityName={setCityName} getData={getData} />
<Data weather={weather} />
</div>;
}
export default App;
Im sending the setCity to the buttons component as a prop.
nevermind the getData() being repeated in useEffect its just for test
buttons component:
function Topbuttons({setCityName,getData}) {
const cities = ['Tehran', 'Paris', 'Newyork', 'Berlin', 'London']
return (
<div className='flex mx-auto py-3 justify-around space-x-8 items-center text-white text-l font-bold'>
{cities.map((v,i) => {
return <button
key={i}
onClick={()=>{
console.log(v)
setCityName(v)
getData()
}}>
{v}
</button>
})}
</div>
)
}
export default Topbuttons
thanks for your help
The problem is that you run setCityName() immediately on click but you run setWeather() after the promise is complete with data. One way to sync them is to run them at the same time after the async promise completes.
Here's a working sandbox of your code. Try using this setup:
app.js
import axios from "axios";
import { useEffect, useState } from "react";
import TopButtons from "./TopButtons";
function App() {
const [cityName, setCityName] = useState("Toronto");
const [weather, setWeather] = useState("");
const getData = (newCityName) => {
console.log("getData newCityName", newCityName);
const url = `https://api.openweathermap.org/data/2.5/weather?q=${newCityName}&units=metric&appid=5476766e86450fff39da1502218a3376`;
axios.get(url).then((res) => {
setCityName(newCityName);
setWeather(res.data);
});
};
useEffect(() => {
getData();
}, []);
return (
<div className="bg-slate-600">
<TopButtons setCityName={setCityName} getData={getData} />
<span>{JSON.stringify(weather)}</span>
</div>
);
}
export default App;
TopButtons.js
function Topbuttons({ getData }) {
const onClick = (newCityName) => getData(newCityName);
const cities = ["Tehran", "Paris", "Newyork", "Berlin", "London"];
return (
<div className="flex mx-auto py-3 justify-around space-x-8 items-center text-white text-l font-bold">
{cities.map((v, i) => {
return (
<button key={i} onClick={() => onClick(v)}>
{v}
</button>
);
})}
</div>
);
}
export default Topbuttons;
The next step would be to detect for a loading state. Since you're using Axios + React, I recommend using the axios-hooks library which uses Axios under-the-hood but provides loading state in hook-format which makes API calls easier to deal with.
What I want is when I click on:
let Afeef = `/${category}`
<Link to={Afeef} className='products-categories'> <h4>{category}</h4></Link>
It should change products according to URL which could be "/electronics","/jewelry" etc but the problem I am facing is that it is changing my URL but the products are not changing. I can't understand what is the problem here. I tried different things but I cant understand it.
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom';
import './Allproducts.css'
import Categories from './categories.json'
import ForYouItem from './ForYouItem'
export default function Allproducts(props) {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch(`https://fakestoreapi.com/products/category/${props.category}`)
.then((res) => res.json())
.then((data) => setProducts(data))
}, [])
const [categories, setCategories] = useState([])
const updateCategory = async ()=> {
const url = "./categories.json"
let data = await fetch(url);
let parsedData = await data.json()
setCategories(parsedData.title)
}
useEffect(() => {
updateCategory();
}, [])
return (
<>
<div className="banner">
<h1>Afeef</h1>
<h4>Get best items in budget</h4>
</div>
<div className="main-grid">
<div className="left-grid">
<div className="left-categories">
<h1>Collections</h1>
{categories.map((category) => {
let Afeef = `/${category}`
return (
<Link to={Afeef} className='products-categories'> <h4>{category}</h4></Link>
)
}
)}
</div>
</div>
<div className="right-grid">
<div className="row ">
{products.map((product) => {
return (
<div className="col-md-4 my-2 Products-1">
<ForYouItem Title={product.title.slice(0, 50)} Price={product.price} Imageurl={product.image} rate={product.rating.rate} count={product.rating.count} />
</div>
)
}
)}
</div>
</div>
</div>
</>
)
}
im gonna try to explain what i understand from your code. So based on the flow of your code, the product can only be fetch once when the page loaded.
but i think in your useEffect that fetch product, you can add the state of Categories in the bracket "[categories]".
then add an onclick setstate product in your link.
so when you click your link, the categories state is updated. then because the bracket inside useEffect that have [categories] is updated the useEffect is run. hence fething new product.
Goal:
Every time when I press the Button 'Test' you always need to fetch fresh data from backend by using API link. Then it should be displayed on the modalform.
Problem:
When I change the text in the input box or delete all text and then closing the modal and then click on the button Test again. The latest changes of what I have done would display. It shouldn't be happening because you always should get the latest data from backend by using API link.
Question:
How should the code always retrieve the data by using api link when you always press on the button 'test'?
Stackblitz:
https://stackblitz.com/edit/react-ts-byxk6x?file=index.tsx
Thank you!
index.tsx
import React, { FC, useState } from 'react';
import { render } from 'react-dom';
import './style.css';
import { TestModalForm } from './TestModalForm';
interface AppProps {}
interface AppState {
name: string;
}
const App: FC<AppProps> = () => {
return (
<div>
<button data-bs-toggle="modal" data-bs-target="#myModal">
Test
</button>
<br />
<TestModalForm />
</div>
);
};
render(<App />, document.getElementById('root'));
TestModalForm.tsx
import React, { useState } from 'react';
export const TestModalForm = () => {
const [inputid, setInputid] = useState('');
const [inputTitle, setInputTitle] = useState('');
React.useEffect(() => {
async function FetchData() {
var data = await fetch(
'https://jsonplaceholder.typicode.com/todos/1'
).then((res) => {
return res.json();
});
setInputid(data.id);
setInputTitle(data.title);
}
FetchData();
}, []);
const handleIdInput = (e: any) => {
setInputid(e.target.value);
};
const handleTitleInput = (e: any) => {
setInputTitle(e.target.value);
};
// Reset Input Field handler
const resetInputField = () => {
setInputid('');
setInputTitle('');
};
return (
<div>
<div
className="modal"
id="myModal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabIndex={-1}
aria-labelledby="staticBackdropLabel"
aria-hidden="true"
>
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h4 className="modal-title">TEST</h4>
<button
type="button"
className="btn-close btn-close-white"
data-bs-dismiss="modal"
></button>
</div>
<div className="modal-body">
<input
type="text"
className="form-control"
placeholder="Id"
value={inputid}
onChange={handleIdInput}
/>
<br />
<input
type="text"
className="form-control"
placeholder="Title"
value={inputTitle}
onChange={handleTitleInput}
/>
<br />
<button className="form-control" onClick={resetInputField}>
Reset
</button>
</div>
</div>
</div>
</div>
</div>
);
};
A simple solution would be to introduce some state to the App component, updated by the test button being clicked, that could be passed to the TestMOdalForm to be used as an useEffect dependency.
It's also anti-pattern to mix async/await with Promise-chains. Pick one or the other.
Example:
const App: FC<AppProps> = () => {
const [id, setId] = useState(0);
return (
<div>
<button
data-bs-toggle="modal"
data-bs-target="#myModal"
onClick={() => setId((c) => c + 1)} // <-- update state upon click
>
Test
</button>
<br />
<TestModalForm id={id} /> // <-- pass state as prop
</div>
);
};
...
const TestModalForm = ({ id }) => { // <-- destructure prop
...
React.useEffect(() => {
async function fetchData() {
try {
const response = await fetch(
'https://jsonplaceholder.typicode.com/todos/1'
);
const data = await response.json();
setInputid(data.id);
setInputTitle(data.title);
} catch(error) {
// handle any fetch rejections or other thrown errors
}
}
fetchData();
}, [id]); // <-- pass prop as dependency
...
return (
...
);
};
Endpoint
const mongoose = require("mongoose");
const CreateBio = mongoose.model("bios");
// exports.baseRoute = async (req, res) => {
// res.send("Server Running");
// };
exports.createBio = async (req, res) => {
console.log(req.body);
let userBio = new CreateBio({
userBio: req.body.userBio
});
console.log('userBio:', userBio);
await userBio.save((err, data) => {
if (err) {
// if there is an error send the following response
res.status(500).json({
message: "Something went wrong, please try again later.",
});
} else {
// if success send the following response
res.status(200).json({
message: "Bio Created",
data,
});
}
});
};
exports.displayBio = async (req, res) => {
// get id from URL by using req.params
let userBioID = req.params.id;
console.log(userBioID);
// we use mongodb's findById() functionality here
await CreateBio.findById({ _id: userBioID }, (err, data) => {
if (err) {
console.log(err)
res.status(500).json({
message: "Something went wrong, please try again later.",
});
} else {
console.log(data);
res.status(200).json({
message: "bio found",
data,
});
}
});
};
Frontend
import React, { useState, useEffect } from "react";
import { useHistory } from "react-router-dom";
import { isExpired, decodeToken } from "react-jwt";
import Form from "react-bootstrap/Form";
import Button from "react-bootstrap/Button";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
import "./tests/home-test.css";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import Navi from "../Navigation/nav";
import Image from "react-bootstrap/Image";
import axios from "axios";
import Nav from "react-bootstrap/Nav";
//import { displayBio } from './displayBio';
// import "./login.css";
const Home = () => {
//const [someProperty, setSomeProperty] = useState([]);
const [userBio, setBio] = useState("")
const history = useHistory();
const loadBio = async () => {
try{
let res = await axios.get('http://localhost:5000/displaybio/:id')
setBio(res.data.data.userBio)
console.log(res.data.data.userBio)
} catch (err){
console.log(err)
}
}
useEffect(() => {
// console.log("use effect working!");
if (!window.localStorage.getItem("token")) {
//redirect to login
console.log("redirect to login");
history.push("/");
}
if (window.localStorage.getItem("token")) {
const isMyTokenExpired = isExpired(window.localStorage.getItem("token"));
console.log(isMyTokenExpired);
if (isMyTokenExpired) {
console.log("redirect to login");
history.push("/");
}
const myDecodedToken = decodeToken(window.localStorage.getItem("token"));
console.log(myDecodedToken);
}
// fetch('http://localhost:5000/displayBio/:id', {
// method: "GET"
// })
// .then(res => res.json())
// .then(response => { setBio(response.item)
// })
loadBio()
}, []);
return (
<div className="Home">
<Container className="homeContainer shadow mt-2">
<Row>
<Col className="d-flex align-items-center">
<span>Home (You are logged in)</span>
</Col>
<Col className="">
<div className="d-flex align-items-center justify-content-end">
<Button
className="logoutBtn mb-2 mt-2"
onClick={(e) => {
window.localStorage.removeItem("token");
this.props.history.push("/");
}}
>
Logout
</Button>
</div>
</Col>
</Row>
<Form>
<Card className="profileCard">
<Card.Body>
<Card.Title className="text-center">
<div>
<Navi />
</div>
<h1>
Welcome Back <span className="text-success">Username</span>
</h1>
</Card.Title>
<Container>
<Row>
<Col className="d-flex justify-content-center col-12">
<div className="profilepic text-center">
Add a Profile Picture here!
</div>
</Col>
<Col className="mt-n5">
<div className="col-12 text-center">
<Card.Text
className="cardText text-center col-lg-10"
value={userBio}
//onChange={setBio}
></Card.Text>
<div className="mt-3">
<Button
className="shareVsBtn"
variant="success"
type="submit"
href="/sharewall"
>
Shared Verse
</Button>
</div>
<div className="mt-3">
<Button
className="postSubBtn mb-3"
variant="success"
type="submit"
href="/postverse"
>
Post a Verse
</Button>
</div>
</div>
</Col>
</Row>
</Container>
</Card.Body>
</Card>
</Form>
</Container>
</div>
);
}
export default Home;
Every time I try to send the request I end up getting a 500 error.500
I cannot seem to get it to console.log any of the information on the front end. I am not sure if I am not just formatting my hook right or not. I am able to get my response on the backend using postman. But my get request from the front end is not going through. Stating that I am having failure at casting {_id :id} at path _id for my model bios.
In order for you to see your userBio you need to have the handlebars inside the Card.Text selector (I omitted the className for this example). This will allow you to see the data in the front end. This is a result by taking the id from mongo and pasting it in the http address i.e: http://localhost:5000/displaybio/123456789.
<Card.Text value={userBio.id}>{userBio}</Card.Text>
In your loadBio you need to console.log(res); to find where the code is in the data object. In this case: setBio(res.data.data.userBio). This the answer to see the data displayed, Cody still needs an answer on how to grab the data dynamically by the id. displaybio/${id} doesn't work.
I am practicing REST API by using one Fake API site. For front-end, I am using React. I successfully login the email and password by using Fake API's login and redirect to list users, where I fetched the data from Fake API and shows the user's name, image. I used the delete button where I can delete the user. I used the Axios delete method but the button does not do anything.
This is the component
import React, { useState, useEffect } from "react";
import axios from "axios";
import { NavLink } from "react-router-dom";
function Users() {
const [state, setState] = useState([]);
useEffect(() => {
fetchingData();
}, []);
const removeData = id => { //THIS IS WHERE I USED THE AXIOS DELETE METHOD
axios
.delete(`https://reqres.in/api/users/${id}`)
.then(res => console.log(res))
.catch(err => console.log(err));
};
const fetchingData = () => {
axios
.get("https://reqres.in/api/users")
.then(response => {
setState(response.data.data);
})
.catch(err => console.log(err));
};
return (
<div>
<div className="col s12 m7">
<h2 className="header">Users</h2>
{state.map(userlist => {
return (
<div className="card horizontal" key={userlist.id}>
<div className="card-image">
<img src={userlist.avatar} alt="images" />
</div>
<div className="card-stacked">
<div className="card-content">
<p>
Name: {userlist.first_name} {userlist.last_name}
</p>
<p>Email: {userlist.email}</p>
</div>
<div className="card-action">
<button
className="btn delete"
onClick={() => removeData !== userlist.id} // THIS IS WHERE I USED THE DELETE LOGIC.
>
Delete
</button>
<NavLink
exact
to={`/api/users/${userlist.id}`}
className="btn edit"
>
Edit
</NavLink>
</div>
</div>
</div>
);
})}
</div>
</div>
);
}
export default Users;
In your delete button's onClick prop, you should pass the id as an argument.
const removeData = async id => {
console.log("ID", id);
try {
const response = await axios.delete(`https://reqres.in/api/users/${id}`);
console.log(response);
fetchingData();
} catch (error) {
console.log(error);
}
};
<button
className="btn delete"
onClick={() => removeData(userlist.id)} // THIS IS WHERE I USED THE DELETE LOGIC.
>
Delete
</button>;