I want to create a module, which keeps an open connection to Flickr API and keeps on receiving new recent photos.
I've tried the 'agentkeepalive' node module, but cloudfront seems to block the requests. This is what my current module code looks like, obviously, the fetch now only runs once:
Server side
require('dotenv').config()
var Flickr = require('flickr-sdk')
var flickr = new Flickr(process.env.FLICKR_API_KEY)
var express = require('express')
var app = express()
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000')
res.header(
'Access-Control-Allow-Headers',
'Origin, X-Requested-With, Content-Type, Accept',
)
next()
})
app.get('/fetch', function(req, res) {
(() =>
flickr.photos
.getRecent()
.then(result => {
return res.send(result.body.photos.photo)
})
.catch(err => {
console.error('Error: ', err)
}))()
})
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
Client side
import './App.css'
import 'bootstrap/dist/css/bootstrap.css'
import React, { Component } from 'react'
import axios from 'axios'
export class App extends Component {
constructor(props) {
super(props)
this.state = {
fetchClicked: false,
photos: [],
}
}
onFetchClick = () => {
this.state.fetchClicked
? this.setState({ fetchClicked: false })
: this.setState({ fetchClicked: true }, () => {
axios.get('http://localhost:5000/fetch').then(response => {
this.setState({
photos: response.data,
})
})
})
}
render() {
const { fetchClicked } = this.state
return (
<div className="App p-5 bg-secondary">
<h1>Flickr Streamer </h1>
{fetchClicked ? (
<button className="btn btn-info" disabled>
Streaming...
</button>
) : (
<button className="btn btn-info " onClick={() => this.onFetchClick()}>
Start the stream!
</button>
)}
<div>{this.state.dataReceived}</div>
<table className="table table-dark mt-5">
<thead>
<tr>
<th scope="col">Photo</th>
<th scope="col">Title</th>
</tr>
</thead>
<tbody>
{this.state.photos.map(item => {
return (
<tr key={item.id}>
<td>
<img
src={`https://farm${item.farm}.staticflickr.com/${item.server}/${item.id}_${item.secret}.jpg`}
/>
</td>
<td scope="row" className="w-50">
{item.title || 'No name'}
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}
}
export default App
Polling is usually the way to do this kind of thing. This is how I would do it:
fetch = () => axios.get('http://localhost:5000/fetch')
.then(({data}) => this.setState({ photos:data })
onFetchClick = () =>
this.setState({ fetchClicked: !this.state.fetchClicked }),
() => this.fetch()
.then(() => setInterval(() => this.fetch(), 15 * 1000))
This doesn't deal with the user clicking the button more than once, but if that callback only executes if the state changed, or you disable the button when it is clicked, that would be handled for you by that. Otherwise, you'd have to handle it.
Related
I am working in MERN project.
what I want
I am fetching the project list from the backend If i click on any project it should give the members list that is working on that project so i want to get the objectid of that clicked project
what i tried
import React, { useState, useEffect } from 'react'
import { NavLink } from 'react-router-dom'
import { useNavigate } from 'react-router-dom';
const AdminDash = () => {
const navigate = useNavigate()
const [userData, setuserData] = useState([])
const [data, setData] = useState({});
const callAboutPage = async () => {
try {
const res = await fetch("/alldata", {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
credentials: "include"
})
const data = await res.json()
setuserData(data)
console.log(setuserData);
if (!res.status === 200) {
const error = new Error(res.error)
throw error
}
} catch (error) {
console.log(error);
navigate("/")
}
}
function handleButtonClick(id) {
fetch(`/api/get-data/${id}`)
.then(response => response.json())
.then(data => {
setData(data);
console.log(data);
});
}
useEffect(() => {
callAboutPage()
}, [])
return (
<>
<div className='container mt-5'>
<div className='row'>
<div className='col-sm-10 col-md-10 col-lg-10'>
<div class="row align-items-md-stretch">
<div class="col-md-6">
<div class="h-100 p-5 text-bg-light rounded-3">
<h2>Current Ongoing Projects</h2>
<ol class="list-group list-group-numbered mt-5">
{
userData.map((item, i) => (
<li class="list-group-item d-flex justify-content-between align-items-start">
<div class="ms-2 me-auto">
<NavLink onClick={() => handleButtonClick()} to="/admindash" className="text-decoration-none"><div class="fw-bold">{item.name}</div></NavLink>
Content for list item
</div>
<i class="bi bi-at"></i>
</li>
))}
</ol>
<label>{data.name}</label>
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
export default AdminDash
backend
app.get('/api/get-data/:id', (req, res) => {
ProjectSchema.findById(req.params.id, (err, doc) => {
if (err) {
res.send(err);
} else {
res.json(doc);
}
});
});
the output i get in my console
{stringValue: '"undefined"', valueType: 'string', kind: 'ObjectId', value: 'undefined', path: '_id', …}
How to achieve this ..any suggestions?
In the mapping onClick pass item.id.
First make sure you getting it in your data while Fetching.
<NavLink onClick={() => handleButtonClick(item.id)} to="/admindash" className="text-decoration-none">{item.name}
I am trying to fetch data from backend, I want to load all data which are in database, when I load function then getting an error like "User.map is not a function", please let me know where am wrong.
User.js
import React, { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { getUsers } from "./apis";
const UserData = () => {
const [users, setUser] = useState([]);
useEffect(() => {
AllUsers();
}, []);
const AllUsers = async () => {
const response = await getUsers();
console.log(response.data);
setUser(response.data ? response.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>
);
};
Api.js
I have added api.js file please check it , and let me know where am wrong
export default UserData;
import Axios from "axios";
const url = "http://localhost:3000/";
export const getUsers = async (id) => {
id = id || "";
return await Axios.get(`${url}/${id}`);
};
export const deleteUser = async (id) => {
return await Axios.delete(`${url}/${id}`);
};
You can use Optional chaining (?.) for check the data is available or not. Because while API's is calling data not set in your state. It's might be a undefined.
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
const dogName = adventurer.dog?.name;
console.log(dogName);
In your case do with this way.
{users?.map((user, index) => (
<tr key={index}>
<th scope="row">{user.id}</th>
<td>{user.title}</td>
<td>{user.description}</td>
</tr>
))}
import Axios from "axios";
const url = "http://localhost:3000/";
export const getUsers = async (id) => {
id = id || "";
return await Axios.get(`${url}/${id}`);
};
export const deleteUser = async (id) => {
return await Axios.delete(`${url}/${id}`);
};
I'm working on a web app to track fruits vendor stock using React.js, MongoDB, Node.js, and Express. I called my database endpoints to render the data in the table. Now I try to increment and decrement the stock amount using a button, but when I try to set the new state, it doesn't work. I try to change the state by clicking, then update the new state in the database. Any advice?
FruitTable component:
import Reac, { Component } from 'react';
import * as ReactBootstrap from 'react-bootstrap';
import axios from 'axios';
import Button from 'react-bootstrap/Button';
class FruitTable extends Component {
constructor(props) {
super(props)
this.state = {
fruits: []
}
this.handleClick = this.handleClick.bind(this)
}
componentDidMount() {
axios.get('http://localhost:5000/fruits/')
.then(res => {
this.setState({
fruits: res.data
});
})
.catch((error) => {
console.log(error)
})
}
handleClick = () => {
const fruits = [...this.state.fruits]
this.setState({fruits: this.state.fruits[1] +1})
}
render() {
return(
<div>
<h1>Fruit Vendor Stock</h1>
<ReactBootstrap.Table striped bordered hover size="sm">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Stock</th>
<th>Price</th>
<th>Add/Reomve</th>
</tr>
</thead>
<tbody>
{
this.state.fruits.map((fruit, index) => (
<tr>
<th scope="row">{index + 1}</th>
<td>{fruit.name}</td>
<td>{fruit.stock}</td>
<td>{fruit.price}</td>
<td>
<div className="mb2">
<Button className="btn btn-primary btn-sm"
onClick={this.handleClick}
>Add</Button>{' '}
<Button className="btn btn-danger btn-sm"
>Remove</Button>
</div>
</td>
</tr>
))
}
</tbody>
</ReactBootstrap.Table>
</div>
)
};
}
export default FruitTable;
FruitsData.model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const fruits = new Schema({
name: { type: String, required: true },
stock: { type: Number, required: true },
price: { type: Number, required: true },
}, {
});
const Fruits = mongoose.model('Fruits', fruits);
module.exports = Fruits;
-Routes:
const router = require('express').Router();
let Fruit = require('../models/fruit_data');
router.route('/').get((req, res) => {
Fruit.find()
.then(fruits => res.json(fruits))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/add').post((req, res) => {
const name = req.body.name;
const stock = Number(req.body.stock);
const price = Number(req.body.price);
const newFruit = new Fruit({
name,
stock,
price,
});
newFruit.save()
.then(() => res.json('Fruit has been added!'))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/:id').get((req, res) => {
Fruit.findById(req.params.id)
.then(Fruit => res.json(Fruit))
.catch(err => res.status(400).json('Error: ' + err));
});
router.route('/:id').delete((req, res) => {
Fruit.findByIdAndDelete(req.params.id)
.then(() => res.json('Fruit has deleted.'))
.catch(err => res.status(400).json("Error: " + err));
});
router.route('/update/:id').put((req, res, next) => {
Fruit.findByIdAndUpdate(req.params.id, {
$set: req.body
}, (error, data) => {
if(error) {
return next(error);
console.log(error)
} else {
res.json(data)
console.log('Stock has been updated')
}
})
})
module.exports = router;
-Server.js:
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDb database connection established successfully!!")
})
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
const fruitsRouter = require('./routes/fruit');
app.use('/fruits', fruitsRouter);
app.listen(port, () => {
console.log(`Server is running on port: ${port}`);
})
-App.js:
import "./App.css";
import React, { Component, useState, useEffect } from "react";
import FruitTable from "./Components/fruitTable";
import "bootstrap/dist/css/bootstrap.min.css";
import { sendEmail } from "./service/emailService";
import axios from 'axios';
function App() {
return (
<div className="App">
<FruitTable />
</div>
);
}
export default App;
The state update in handleClick isn't correct, it is taking the 1th element value, adding 1 to it, and setting this as the this.state.fruits state value. This breaks the state invariant of fruits being an array. This is likely the "crash" you are seeing as a non-array value is attempted to be mapped.
handleClick = () => {
const fruits = [...this.state.fruits]
this.setState({
fruits: this.state.fruits[1] +1 // <-- mutates state invariant!!
})
}
The code also isn't passing a reference to which index or fruit it is trying to update.
According to your schema the fruit items have 3 properties, name, stock, and price. For this solution I'll assume that name is sufficiently unique for identification purposes and that you are incrementing/decrementing the stock quantity.
handleClick = (name, addQuantity = 1) => () => {
this.setState(prevState => ({
fruits: prevState.fruits.map(fruit =>
fruit.name === name
? { ...fruit, stock: fruit.stock + addQuantity}
: fruit
)
}));
}
Now you need to pass the currently mapped fruit item's name property to the handler when mapping.
{this.state.fruits.map((fruit, index) => (
<tr key={fruit.name}>
<th scope="row">{index + 1}</th>
<td>{fruit.name}</td>
<td>{fruit.stock}</td>
<td>{fruit.price}</td>
<td>
<div className="mb2">
<Button
className="btn btn-primary btn-sm"
onClick={this.handleClick(item.name, 1)} // <-- add one
>
Add
</Button>{' '}
<Button
className="btn btn-danger btn-sm"
onClick={this.handleClick(item.name, -1)} // remove one
>
Remove
</Button>
</div>
</td>
</tr>
))}
so I'm making a mini eCommerce app using MERN stack, i'm fetching products for each seller using his id, so he's the only one who can edit or delete his own products,
in my component i get the user's id from redux state from the user, then i use the id to fetch products for each logged in seller.(in useEffect)
so fetching products depends on the user, and the user is always loaded and no need to fetch it after he login.
the problem is, only the first time after i login and i render the component it gives me
TypeError: products.map is not a function. but if i refresh the page it works fine
so it doesn't see products the first time idk why even if the user is there and the id to fireup the fetching function.
function EditProducts() {
const { user } = useSelector(state => state.userrr);
const { loading, products } = useSelector(state => state.userProductsss);
const dispatch = useDispatch();
useEffect(() => {
console.log(user);
console.log(products);
if (!user) {
return;
} else {
let id = user._id;
dispatch(fetchUserProducts(id));
}
}, [dispatch, user]);
const deleteIt = id => {
dispatch(deleteProduct(id))
.then(res => {
toast.success(res, { position: toast.POSITION.BOTTOM_LEFT });
})
.catch(error => {
toast.error(error, {
position: toast.POSITION.BOTTOM_LEFT,
autoClose: false
});
});
};
console.log(products);
return (
<Container>
<Table striped bordered hover variant='dark'>
<thead>
<tr>
<th>category</th>
<th>Description</th>
<th>Price</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
{loading && (
<tr>
<td colSpan='4'>
<Spinner animation='border' /> loading...{" "}
</td>
</tr>
)}
{!user && !loading && (
<tr>
<td colSpan='4'>Please Log in to access this page</td>
</tr>
)}
{products.map(product => (
<tr key={product._id}>
<td>{product.name}</td>
<td>{product.description}</td>
<td>${product.price}</td>
<td>
<span className='btn btn-primary mr-3'>
<UpdateProductForm
id={product._id}
name={product.name}
description={product.description}
category={product.category}
price={product.price}
numberInStock={product.numberInStock}
productImage={product.productImage}
/>
</span>
<Button className='btn btn-danger' onClick={() => deleteIt(product._id)}>
<FontAwesomeIcon icon={faTrash} />
</Button>
</td>
</tr>
))}
</tbody>
</Table>
</Container>
);
}
export default EditProducts;
this is my reducer
const productReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_USER_PRODUCTS_STARTED:
return {
...state,
loading: true
};
case FETCH_USER_PRODUCTS_SUCCESS:
return {
...state,
loading: false,
error: null,
products: action.payload.products
};
case FETCH_USER_PRODUCTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
success: null
};
default:
return state;
}
};
this is the actions
export const fetchUserProducts = userId => {
return dispatch => {
dispatch(fetchUserProductsStarted());
axios
.get(`/api/product/${userId}/products`)
.then(res => {
dispatch(fetchUserProductsSuccess(res.data));
})
.catch(error => {
dispatch(fetchUserProductsFailure(error.message));
});
};
};
const fetchUserProductsStarted = () => {
return {
type: FETCH_USER_PRODUCTS_STARTED
};
};
const fetchUserProductsSuccess = products => {
return {
type: FETCH_USER_PRODUCTS_SUCCESS,
payload: {
products
}
};
};
const fetchUserProductsFailure = error => {
return {
type: FETCH_USER_PRODUCTS_FAILURE,
payload: {
error
}
};
};
so the problem was that useEffect couldn't be able to ensure the user data is loaded before the first render here:
const { user } = useSelector(state => state.userrr);
so the user was null, so it couldn't get the products depending on the user id.
what i did is that i loaded the user again inside the component useEffect so it gets the user data.
useEffect(() => {
dispatch(loadUser());
const id = user ? user._id : null;
dispatch(fetchUserProducts(id));
}, [ dispatch, id]);
I'm building a TODO list and one of the things that it needs to do is delete.
Here is my server.js code
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const cpdRoutes = express.Router();
const PORT = 4000;
let Cpd = require("./cpd.model");
app.use(cors());
app.use(bodyParser.json());
//connects my backend to my mongo database
mongoose.connect('mongodb://127.0.0.1:27017/cpds', { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', function() {
console.log("MongoDB database connection established successfully");
})
cpdRoutes.route('/').get(function(req, res) {
Cpd.find(function(err, cpds) {
if (err) {
console.log(err);
}
else {
res.json(cpds);
}
});
});
//finds the data by id
cpdRoutes.route('/:id').get(function(req, res) {
let id = req.params.id;
Cpd.findById(id, function(err, cpd) {
res.json(cpd);
});
});
//creating data
cpdRoutes.route('/add').post(function(req, res) {
let cpd = new Cpd(req.body);
cpd.save()
.then(cpd => {
res.status(200).json({'cpd': 'New data added successfully'});
})
.catch(err => {
res.status(400).send('Adding new data failed');
});
});
//update data
cpdRoutes.route('/update/:id').post(function(req, res) {
Cpd.findById(req.params.id, function(err, cpd) {
if (!cpd)
res.status(404).send("data is not found");
else
cpd.cpd_date = req.body.cpd_date;
cpd.cpd_activity = req.body.cpd_activity;
cpd.cpd_hours = req.body.cpd_hours;
cpd.cpd_learningStatement = req.body.cpd_learningStatement;
cpd.save().then(cpd => {
res.json('Data updated!');
})
.catch(err => {
res.status(400).send("Update not possible");
});
});
});
// cpdRoutes.route('/delete/:id').post(function(req, res) {
// Cpd.findById(req.params.id, function(err, cpd) {
// if (!cpd)
// res.status(404).send("data is not found");
// else
// cpd.cpd_date = req.body.cpd_date;
// cpd.cpd_activity = req.body.cpd_activity;
// cpd.cpd_hours = req.body.cpd_hours;
// cpd.cpd_learningStatement = req.body.cpd_learningStatement;
// cpd.save().then(cpd => {
// res.json('Data updated!');
// })
// .catch(err => {
// res.status(400).send("Update not possible");
// });
// });
// });
cpdRoutes.route.get('/delete', function(req, res){
var id = req.query.id;
Cpd.find({_id: id}).remove().exec(function(err, expense) {
if(err)
res.send(err)
res.send('Data successfully deleted!');
})
});
app.use('/cpds', cpdRoutes);
app.listen(PORT, function() {
console.log("Server is running on Port: " + PORT);
});
My delete component:
import React from 'react';
import axios from 'axios';
import { Button } from 'react-bootstrap';
import { Link } from 'react-router-dom';
class DeleteCpd extends React.Component {
constructor(){
super();
this.state={id:''};
this.onClick = this.onClick.bind(this);
this.delete = this.delete.bind(this);
}
// componentDidMount() {
// this.setState({
// id: this.props.cpds.id
// })
// }
componentDidMount() {
axios.get('http://localhost:4000/cpds/'+this.props.match.params.id)
.then(response => {
this.setState({
cpd_date: response.data.cpd_date,
cpd_activity: response.data.cpd_activity,
cpd_hours: response.data.cpd_hours,
cpd_learningStatement: response.data.cpd_learningStatement
})
})
.catch(function (error) {
console.log(error);
})
}
onClick(e){
this.delete(this);
}
delete(e){
axios.get('http://localhost:4000/cpds/'+this.props.match.params.id)
.then(function(response) {
});
}
render(){
return (
<Button onClick={this.onClick}>
<Link to={{pathname: '/', search: '' }} style={{ textDecoration: 'none' }}>
<span className="glyphicon glyphicon-remove"></span>
</Link>
</Button>
)
}
}
export default DeleteCpd;
and my App.js:
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import CreateCpd from "./components/create-cpd.component";
import EditCpd from "./components/edit-cpd.component";
import CpdsList from "./components/cpds-list.component";
import DeleteCpd from "./components/cpds-delete.component";
class App extends Component {
render() {
return (
<Router>
<div className="container">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<Link to="/" className="navbar-brand">MERN-Stack Cpd tracker App</Link>
<div className="collpase navbar-collapse">
<ul className="navbar-nav mr-auto">
<li className="navbar-item">
<Link to="/" className="nav-link">Data List</Link>
</li>
<li className="navbar-item">
<Link to="/create" className="nav-link">Create Cpd data</Link>
</li>
</ul>
</div>
</nav>
<br/>
<Route path="/" exact component={CpdsList} />
<Route path="/edit/:id" component={EditCpd} />
<Route path="/delete/:id" component={DeleteCpd} />
<Route path="/create" component={CreateCpd} />
</div>
</Router>
);
}
}
export default App;
This is the error my getting:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in CpdList (created by Context.Consumer)
What I'm trying to do is delete via id. What am I doing wrong?
This is my CPDList:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';
// import { CSVLink } from "react-csv";
// import DeleteCpd from './cpd_delete.component';
const Cpd = props => (
<tr>
<td>{props.cpd.cpd_date}</td>
<td>{props.cpd.cpd_activity}</td>
<td>{props.cpd.cpd_hours}</td>
<td>{props.cpd.cpd_learningStatement}</td>
<td>{props.cpd.cpd_evidence}</td>
<td>
<Link to={"/edit/"+props.cpd._id}>Edit</Link>
</td>
<td>
<Link to={"/delete/"+props.cpd._id}>Delete(not working yet)</Link>
</td>
</tr>
)
export default class CpdList extends Component {
constructor(props) {
super(props);
this.state = {
cpds: [],
// csvData:[
// {
// "date": ""
// },
// {
// "activity": ""
// },
// {
// "hours": ""
// },
// {
// "learningStatement": ""
// },
// {
// "evidence": ""
// }
// ]
};
};
// exportCsv()
// {
// var csvRow=[];
// }
componentDidMount() {
axios.get('http://localhost:4000/cpds/')
.then(response => {
this.setState({ cpds: response.data });
})
.catch(function (error){
console.log(error);
});
};
componentDidUpdate() {
axios.get('http://localhost:4000/cpds/')
.then(response => {
this.setState({ cpds: response.data });
})
.catch(function (error){
console.log(error);
});
}
cpdList() {
return this.state.cpds.map(function(currentCpd, i){
return <Cpd cpd={currentCpd} key={i} />;
});
}
render() {
return(
<div>
<h3>Cpd Data List</h3>
<table className="table table-striped" style={{ marginTop: 20 }} >
<thead>
<tr>
<th>Date</th>
<th>Activity</th>
<th>Hours</th>
<th>Learning Statement</th>
<th>Evidence</th>
</tr>
</thead>
<tbody>
{ this.cpdList() }
</tbody>
</table>
{/* <CSVLink data={csvData}
filename={"db.csv"}
color="primary"
style={{float: "left", marginRight: "10px"}}
className="btn btn-primary"
>Download .CSV
</CSVLink> */}
</div>
)
}
};
please ignore the commented out code still working on that.