I get the path of the image displayed on my react app instead of the actual image and on my react-native app the images don't get displayed at all - node.js

Question number 1: I've created a CRUD (CMS) app with react where you create articles. My backend is in node.js and my DB is in MySQL. I also have a react-native app that pulls all the articles I created on my CMS. Where do I store the images I add to my articles? In a folder in my backend? Or somewhere else?
Question number 2: So I've connected to my db and I'm displaying on my CMS react web page the title, content and image. And yet when it comes to the image, you can only see the path ie. (C:\fakepath\Screenshot 2020-06-14 at 23.07.52.png), not the actual image. I don't know if the issue is with my backend but after a bit of online research a lot of people said that you need to add require in the src if you want the actual image displayed and not just the path, a bit like this:
<img src={require('./logo.jpeg')} />
However with the way I've done it I don't see how I can use the img tag and add src because I'm fetching image to render from the backend and hence, not creating the img tag. Hope this makes sense.
ViewAllArticles.js
class ViewAllArticles extends Component {
state = {
articles: []
}
getArticles = _ => {
fetch('http://localhost:4000/articles')
.then(response => response.json())
.then(response => this.setState({ articles: response.data }))
.catch(err => console.error(err))
}
componentDidMount() {
this.getArticles();
}
renderArticle = ({ id, title, image }) => <div key={id}>{title}, {image}</div>
render() {
const { articles } = this.state;
return (
<div>
<h1>Home</h1>
<div>
{articles.map(this.renderArticle)}
</div>
</div>
);
}
}
export default ViewAllArticles;
If the require is not what's missing do you have any other ideas of why this is happening?
I'm also pulling the same data for my react-native app and the images don't come up.
This is the code in my react-native app:
largeTitle={item.title} works fine and displays all the titles on the cards but source={item.image} doesn't display the images on the same cards.
HomeFeed.js
import React, { Component } from "react";
import { StyleSheet, Text, View, FlatList } from 'react-native';
import {AppleCard, AppOfTheDayCard} from 'react-native-apple-card-views';
export default class HomeFeed extends Component {
constructor() {
super()
this.state = {
dataSource: []
}
}
// https://github.com/WrathChaos/react-native-apple-card-views
renderItem = ({ item }) => {
return (
<View>
<View style={styles.card}>
<AppleCard
largeTitle={item.title}
footnoteText="subtitle placeholder"
source={item.image}
>
</AppleCard>
</View>
</View>
)
}
componentDidMount() {
const url = 'http://localhost:4000/articles'
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.data
})
})
}
render() {
return(
<View style={styles.homeFeed}>
<FlatList
data={this.state.dataSource}
renderItem={this.renderItem}
/>
</View>
);
}
}
const styles = StyleSheet.create({
homeFeed: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center'
},
card: {
padding: 15
}
});
This is my backend code in node.js
index.js
const express = require('express');
const cors = require('cors');
const mysql = require('mysql');
const { query } = require('express');
const multer = require('multer');
const upload = multer({dest: 'public/images'}); // uploaded article image here
const app = express();
//all queries go here
const SELECT_ALL_ARTICLES_QUERY = 'SELECT * FROM articles';
//create connection
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'DidiLydiBibi96',
database: 'myTherapy'
});
connection.connect(err => {
if(err) {
return err;
}
});
//end of creating connection
app.use(cors());
app.get('/', (req, res) => {
res.send('go to /articles to see articles')
});
//ROUTES
//Add new article
app.use('/image', express.static('public/images'));
app.get('/articles/add', upload.single('image'), (req, res) => {
const { title, content, image } = req.query; //fields from db
const INSERT_ARTICLES_QUERY = `INSERT INTO articles (title, content, image) VALUES(?, ?, ?)`;
connection.query(INSERT_ARTICLES_QUERY, [title, content, image], (err, results) => {
if(err) {
return res.send(err)
}
else {
return res.send('successfully added article')
}
});
});
//View all articles
app.get('/articles', (req, res) => {
connection.query(SELECT_ALL_ARTICLES_QUERY, (err, results) => {
if(err) {
return res.send(err)
}
else {
return res.json({
data: results
})
}
});
});
app.listen(4000, () => {
console.log('Articles server listening on port 4000')
});

For the first problem, getting the image uri from the server is good enough, and it's your react application that should handle converting that to a visible image.
One way to do this would be to change your renderArticles method like :
renderArticle = ({ id, title, image }) => (
<div key={id}>
{title}
<div
style={{
width: "100px",
height: "100px",
backgroundSize: "cover",
backgroundImage: "url(" + image + ")"
}}
></div>
</div>
);

Related

I want to get image from backend to frontend in MERN Stack

I want to get image from backend to frontend in MERN Stack. all data gets about product also gets image title but images cannot render.
I want to get image from backend to frontend in MERN Stack. all data gets about product also gets image title but images cannot render
I want to get image from backend to frontend in MERN Stack. all data gets about product also gets image title but images cannot render
I want to get image from backend to frontend in MERN Stack. all data gets about product also gets image title but images cannot render
This is my frontend code
import "./Related.css";
import { NavLink } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { getMovies } from "../../../Redux/Actions/MoviesAction";
function Related() {
const dispatch = useDispatch();
const moviesList = useSelector((state) => state.MoviesReducer.list);
useEffect(() => {
withoutReload();
});
function withoutReload() {
dispatch(getMovies());
}
const [relatedProducts, setRelatedProducts] = useState(10);
const allProducts = moviesList.slice(0, relatedProducts);
const loadMoreProducts = () => {
setRelatedProducts(relatedProducts + relatedProducts);
};
return (
<>
<div id="related">
<div className="moviesContainer">
{allProducts.map((data, index) => {
return (
<div className="movies" key={index}>
<img src={data.picture} alt={data.picture} />
<div>
<h5 className="mb-0">{data.title}</h5>
<div className="cat">{data.category}</div>
<NavLink to={`/download-movie/${data._id}`}>View</NavLink>
</div>
</div>
);
})}
</div>
<div className="cancelBtn">
<button onClick={() => loadMoreProducts()}>Load More</button>
</div>
</div>
</>
);
}
export default Related;
This is my Backend code
const express = require("express");
const router = express.Router();
const addNewMoviesData = require("../Models/AddNewMovie");
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "images/");
},
filename: function (req, file, cb) {
cb(null, file.originalname);
},
});
const upload = multer({ storage: storage });
router.post("/addNewMovie", upload.single("picture"), (req, res) => {
const addNewMoviesform = new addNewMoviesData({
picture: req.file.originalname,
title: req.body.title,
genre: req.body.genre,
duration: req.body.duration,
releaseDate: req.body.releaseDate,
size: req.body.size,
language: req.body.language,
description: req.body.description,
link: req.body.link,
category: req.body.category,
subCategory: req.body.subCategory,
});
addNewMoviesform
.save()
.then((data) => {
res.json(data);
})
.catch((error) => {
console.log(error);
});
});

Why does my DataForm not return as an object that my URL.createObjectURL is supposed to read?

All I am trying to accomplish is to give my users the option to upload images. I decided to use mongoDB as my database which means I must store photos locally and then send them to the DB. As far as I know, I am new. The object created by ImageName and ImageData isn't being passed properly to my axios post request
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
Here is my route
const Image = require('../models/imageModel');
const ImageRouter = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb){
cb(null, './uploads/')
},
filename: function(req,file,cb){
cb(null, Date.now() + file.originalname);
}
});
const fileFilter = (req, file, cb) =>{
if(file.mimetype === 'image/jpeg' || file.mimetype ==='image/png'){
cb(null, true);
} else{
//rejects storing file
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits:{
fileSize:1024 *1024 * 5
},
fileFilter: fileFilter
});
// stores image in uploads folder using multers and creates a reference to the file
ImageRouter.route("/upload")
.post(upload.single('imageData'), (req, res, next) => {
console.log(req.body);
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.file.path
});
newImage.save()
.then((result)=>{
console.log(result)
res.status(200).json({
success: true,
document: result
});
})
.catch((err)=> next(err))
});
module.exports = ImageRouter;
here is my model
const Schema = mongoose.Schema;
const ImageSchema = new Schema({
imageName:{
type: String,
default: "none",
required: true
},
imageData: {
type :String,
required: false
}
});
const Image = mongoose.model('Image' , ImageSchema)
module.exports = Image;
Here is my ImageUploader page that calls the function
import axios from 'axios';
import DefaultImg from '../assets/default-img.jpg';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ImageUploader extends Component {
constructor(props) {
super(props)
this.state = {
multerImage: DefaultImg
}
};
setDefaultImage = (uploadType) => {
if (uploadType === "multer") {
this.setState({multerImage: DefaultImg});
};
};
// function to upload image once it has been captured include multer and
// firebase methods
uploadImage(e, method) {
let imageObj = {};
if (method === "multer") {
let imageFormObj = new FormData();
imageFormObj.append("imageName", "multer-image-" + Date.now());
imageFormObj.append("imageData", e.target.files[0]);
console.log(imageFormObj)
// stores a readable instance of the image being uploaded using multer
this.setState({
multerImage: URL.createObjectURL(e.target.files[0])
});
axios
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
.catch((err) => {
alert("Error while uploading image using multer");
this.setDefaultImage("multer");
});
}
};
render() {
return (
<div className="main-container">
<h3 className="main-heading">Image Upload App</h3>
<div className="image-container">
<div className="process">
<h4 className="process__heading">Process: Using Multer</h4>
<p className="process__details">Upload image to a node server, connected to a
MongoDB database, with the help of multer</p>
<input
type="file"
display="block"
className="process__upload-btn"
placeholder="Username"
onChange={(e) => this.uploadImage(e, "multer")
}/>
<img
src={this.state.multerImage}
alt="upload-image"
className="process__image"/>
</div>
</div>
</div>
);
};
};
and finally this is where it's being rendered
// import {EditProfile} from './EditProfile'
import DisplayCats from '../cats/DisplayCats'
// import Button from '#material-ui/core/Button';
import Axios from 'axios';
import ImageUploader from '../ImageUploader';
function ProfilePage(props) {
console.log(props.userInfo)
const user = props.userInfo
console.log(user)
console.log(user._id)
console.log(props.userInfo._id)
let [responseData,
setResponseData] = useState('');
// getLocation = () => {
// navigator
// .geolocation
// .getCurrentPosition(function (position) {
// console.log(position)
// });
// }
const clickHandler = (e) => {
this
.props
.history
.push('/DisplayCats')
}
// const setProfileImage = (event) => {
// Axios
// .post('http://localhost:5000/api/users/updateImage/' + user._id, {
// "_id": user._id,
// "profileImage": event.target.value
// })
// .then(res => {
// setResponseData(res.data)
// console.log(responseData)
// }, function (err) {
// console.log(err)
// })
// }
return (
<div style={{
color: "black"
}}>
<h5>This is {props.userInfo.firstName}'s Profile Page</h5>
<h5>Last name: {props.userInfo.lastName}</h5>
<h5>Age: {props.userInfo.age}</h5>
<h5>Location:{props.userInfo.location}</h5>
<h5>Image:{props.userInfo.profileImage}</h5>
<h5>Biography:{props.userInfo.biography}</h5>
<ImageUploader user={user}/>
{/* <div className="col-md-6 img">
<img
src={responseData.profileImage}
alt="profile image"
className="img-rounded"/>
</div> */}
<div className="row">
<DisplayCats user={user}/>
</div>
{/*
<Button
variant="outlined"
color="primary"
onClick={this.clickHandler}
component={this.EditProfile}
user={this.props.user}>
Edit Info
</Button> */}
</div>
)
}
export default ProfilePage;
I want my image data and and image name to be created into a URL for my other functions to read it. My error comes back as POST /api/image/uploadmulter/ 404 97.290 ms - 163
Perhaps im missing something but from the code you shared, you set your route as:
ImageRouter.route("/upload")
so, your client side code should be posting to: http://localhost:5000/upload
yet, your code does this:
.post(`http://localhost:5000/api/image/uploadmulter/`
You're getting a 404 error, which makes sense since this route hasn't been defined.
On a related note, I'd recommend using something like react-uploady, to manage the uploads on your client-side. It will save you a lot of code and bugs. Especially if you want to show preview or other related functionality (like: progress, cancel, retry, etc.).

Cannot add to cart on frontend react.js, although the end points are working correctly and it is adding items to cart in post man app

so I am trying to add to cart. My node.js endpoints are working correctly and I am able to add items to cart when viewed in postman app but it does not display items on the front end, and when inspecting through the chrome developers tools, the items array is empty when on the postman while testing it is successfully storing items.
Here is my server.js
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
let Book = require("./models/bookModel");
const port = 4000;
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/books", { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once("open", function() {
console.log("MongoDB database connection established successfully..");
});
const bookRoutes = express.Router();
app.use("/books", bookRoutes);
const cartRoutes = express.Router();
app.use("/cart", cartRoutes);
bookRoutes.route("/").get(function(req, res) {
Book.find(function(err, books) {
if (err) {
console.log(err);
} else {
res.json(books);
}
});
});
bookRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Book.findById(id, function(err, book) {
res.json(book);
});
});
cartRoutes.route("/").get(function(req, res) {
var cart = req.session.cart;
var displayCart = { items: [], total: 0 };
var total = 0;
for (var item in cart) {
displayCart.items.push(cart[item]);
total += cart[item].qty * cart[item].price;
}
displayCart.total = total;
return res.json(displayCart);
});
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
Book.findById(id, function(err, book) {
if (err) {
console.log(err);
}
if (cart[id]) {
cart[id].qty++;
} else {
cart[id] = {
item: book._id,
title: book.title,
price: book.price,
qty: 1
};
}
res.redirect("/cart");
});
});
app.listen(port, function() {
console.log("Server is running on Port: " + port);
});
the server response:
{
"items": [
{
"item": "5dd7668f33c21d811b74f403",
"title": "Modern PHP",
"price": 25.65,
"qty": 1
},
{
"item": "5dd6bb36725bbba1ca482eea",
"title": "Professional Node.js",
"price": 20.56,
"qty": 2
}
],
"total": 66.77
}
cart.js
import React, { Component } from "react";
import axios from "axios";
import CartItem from "./cart1-item.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import { throws } from "assert";
export default class Cart extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/cart/")
.then(response => {
this.setState({
items: response.data.items
});
console.log(response.data.items);
})
.catch(function(err) {
console.log(err);
});
}
checkItems() {
return this.state.items.map((currItem, i) => {
return <CartItem book={currItem} key={i}></CartItem>;
});
}
Calculate = item => {
return item.qty * item.price;
};
render() {
return (
<div className="container">
<div className="row">{this.checkItems()}</div>
</div>
);
}
}
cartitem.js
import React, { Component } from "react";
import "bootstrap/dist/css/bootstrap.min.css";
const CartItem = props => {
return (
<div className="container">
<h2>{props.book.title}</h2>
</div>
);
};
export default CartItem;
here is the app.js code for cart route
<Route path="/cart" exact component={Cart}></Route>
Edited code book-details.component.js
import React, { Component } from "react";
import "../css/styles.css";
import axios from "axios";
export default class BookDetails extends Component {
constructor(props) {
super(props);
this.state = {
book: []
};
}
componentDidMount() {
axios
.get("http://localhost:4000/books/" + this.props.match.params.id)
.then(response => {
this.setState({ book: response.data });
})
.catch(function(err) {
console.log(err);
});
}
AddToCart = e => {
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`).then(() => {
window.location.href = "http://localhost:3000/cart/";
});
};
render() {
const { book, quantity } = this.state;
return (
<div className="container">
<div className="row">
<div className="col sm-4">
<img src={`./images/${book.cover}`}></img>
</div>
<div className="col sm-8">
<h2>{book.title}</h2>
<ul>
<li>Category: {book.category}</li>
<li>Author: {book.author}</li>
</ul>
<p className="button blue">${book.price}</p>
<p>{book.description}</p>
<button id={book._id} onClick={this.AddToCart}>
Add To Cart
</button>
</div>
</div>
</div>
);
}
}
App.js
import React, { Component } from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import BooksList from "./components/book-list.component.js";
import BookDetails from "./components/book-details.component.js";
import "bootstrap/dist/css/bootstrap.min.css";
import Navigation from "./components/navigation.component";
import Cart from "./components/cart1.component";
class App extends Component {
render() {
return (
<Router>
<Navigation></Navigation>
<Route
path="/"
exact
render={() => (
<div className="container">
<BooksList></BooksList>
</div>
)}
></Route>
<Route path="/books/:id" exact component={BookDetails}></Route>
<Route path="/cart/" exact component={Cart}></Route>
</Router>
);
}
}
export default App;
Any help would be appreciated.
i think i should point out that what you are passing to CartItem is "books" while in the CartItem component you are trying to get "items" from props (this.props.items). that part should be this.props.books.title.
UPDATE:
After you updated your question, i noticed this addition:
and on clicking the add to cart button it navigates to the link
href={"/cart"} className="button"> Add to Cart
this might be where your problem is coming from. on the API, to add books to cart, you did something like this:
cartRoutes.route("/:id").post(function(req, res) {
req.session.cart = req.session.cart || {};
var cart = req.session.cart;
let id = req.params.id;
so you are basically making a post request (even though from the code you are not really posting any data since you are just extracting the id from the url parameter. maybe you should consider making this a get request).
the key part here is the post http method and the id that is expected as the url parameter.
to make things simple on yourself, you can change your "add to cart" to something like:
<button className="button" id={book.id} onClick={this.addToCart}>
Add to Cart
</button>
for addToCart, you can do something like this:
addToCart=(e)=>{
let id = e.currentTarget.getAttribute("id");
axios.post(`http://localhost:4000/cart/${id}`)
.then(()=>{window.location.href = "http://localhost:3000/cart"})
}
note that like i said, you can replace the post request above to a get request since you are not actually posting any form data. if you wish to do this, you should also change the corresponding post request in your api to a get request.
Also, note that you can't get cart items posted through postman from the browser. you are using node sessions for cart items storage. you have to create a different react component (if you have not already created it) from where you can send the post request above to your express api

Upload image file from React front end to Node/Express/Mongoose/MongoDB back end (not working)

I’ve spent most of a day looking into this and trying to make it work. This is an app with a React/Redux front end, and a Node/Express/Mongoose/MongoDB back end.
I currently have a Topics system where an authorized user can follow/unfollow topics, and an admin can Add/Remove topics.
I want to be able to upload an image file when submitting a new topic, and I want to use Cloudinary to store the image and then save the images path to the DB with the topic name.
The problem I am having is that I am unable to receive the uploaded file on the back end from the front end. I end up receiving an empty object, despite tons of research and trial/error. I haven’t finished setting up Cloudinary file upload, but I need to receive the file on the back end before even worrying about that.
SERVER SIDE
index.js:
const express = require("express");
const http = require("http");
const bodyParser = require("body-parser");
const morgan = require("morgan");
const app = express();
const router = require("./router");
const mongoose = require("mongoose");
const cors = require("cors");
const fileUpload = require("express-fileupload");
const config = require("./config");
const multer = require("multer");
const cloudinary = require("cloudinary");
const cloudinaryStorage = require("multer-storage-cloudinary");
app.use(fileUpload());
//file storage setup
cloudinary.config({
cloud_name: "niksauce",
api_key: config.cloudinaryAPIKey,
api_secret: config.cloudinaryAPISecret
});
const storage = cloudinaryStorage({
cloudinary: cloudinary,
folder: "images",
allowedFormats: ["jpg", "png"],
transformation: [{ width: 500, height: 500, crop: "limit" }] //optional, from a demo
});
const parser = multer({ storage: storage });
//DB setup
mongoose.Promise = global.Promise;
mongoose.connect(
`mongodb://path/to/mlab`,
{ useNewUrlParser: true }
);
mongoose.connection
.once("open", () => console.log("Connected to MongoLab instance."))
.on("error", error => console.log("Error connecting to MongoLab:", error));
//App setup
app.use(morgan("combined"));
app.use(bodyParser.json({ type: "*/*" }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
router(app, parser);
//Server setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);
console.log("server listening on port: ", port);
TopicController/CreateTopic
exports.createTopic = function(req, res, next) {
console.log("REQUEST: ", req.body); //{ name: 'Topic with Image', image: {} }
console.log("IMAGE FILE MAYBE? ", req.file); //undefined
console.log("IMAGE FILES MAYBE? ", req.files); //undefined
const topic = new Topic(req.body);
if (req.file) {
topic.image.url = req.file.url;
topic.image.id = req.file.publid_id;
} else {
console.log("NO FILE UPLOADED");
}
topic.save().then(result => {
res.status(201).send(topic);
});
};
router.js
module.exports = function(app, parser) {
//User
app.post("/signin", requireSignin, Authentication.signin);
app.post("/signup", Authentication.signup);
//Topic
app.get("/topics", Topic.fetchTopics);
app.post("/topics/newTopic", parser.single("image"), Topic.createTopic);
app.post("/topics/removeTopic", Topic.removeTopic);
app.post("/topics/followTopic", Topic.followTopic);
app.post("/topics/unfollowTopic", Topic.unfollowTopic);
};
CLIENT SIDE
Topics.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Loader, Grid, Button, Icon, Form } from "semantic-ui-react";
import {
fetchTopics,
followTopic,
unfollowTopic,
createTopic,
removeTopic
} from "../actions";
import requireAuth from "./hoc/requireAuth";
import Background1 from "../assets/images/summer.jpg";
import Background2 from "../assets/images/winter.jpg";
const compare = (arr1, arr2) => {
let inBoth = [];
arr1.forEach(e1 =>
arr2.forEach(e2 => {
if (e1 === e2) {
inBoth.push(e1);
}
})
);
return inBoth;
};
class Topics extends Component {
constructor(props) {
super(props);
this.props.fetchTopics();
this.state = {
newTopic: "",
selectedFile: null,
error: ""
};
}
onFollowClick = topicId => {
const { id } = this.props.user;
this.props.followTopic(id, topicId);
};
onUnfollowClick = topicId => {
const { id } = this.props.user;
this.props.unfollowTopic(id, topicId);
};
handleSelectedFile = e => {
console.log(e.target.files[0]);
this.setState({
selectedFile: e.target.files[0]
});
};
createTopicSubmit = e => {
e.preventDefault();
const { newTopic, selectedFile } = this.state;
this.props.createTopic(newTopic.trim(), selectedFile);
this.setState({
newTopic: "",
selectedFile: null
});
};
removeTopicSubmit = topicId => {
this.props.removeTopic(topicId);
};
renderTopics = () => {
const { topics, user } = this.props;
const followedTopics =
topics &&
user &&
compare(topics.map(topic => topic._id), user.followedTopics);
console.log(topics);
return topics.map((topic, i) => {
return (
<Grid.Column className="topic-container" key={topic._id}>
<div
className="topic-image"
style={{
background:
i % 2 === 0 ? `url(${Background1})` : `url(${Background2})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover"
}}
/>
<p className="topic-name">{topic.name}</p>
<div className="topic-follow-btn">
{followedTopics.includes(topic._id) ? (
<Button
icon
color="olive"
onClick={() => this.onUnfollowClick(topic._id)}
>
Unfollow
<Icon color="red" name="heart" />
</Button>
) : (
<Button
icon
color="teal"
onClick={() => this.onFollowClick(topic._id)}
>
Follow
<Icon color="red" name="heart outline" />
</Button>
)}
{/* Should put a warning safety catch on initial click, as to not accidentally delete an important topic */}
{user.isAdmin ? (
<Button
icon
color="red"
onClick={() => this.removeTopicSubmit(topic._id)}
>
<Icon color="black" name="trash" />
</Button>
) : null}
</div>
</Grid.Column>
);
});
};
render() {
const { loading, user } = this.props;
if (loading) {
return (
<Loader active inline="centered">
Loading
</Loader>
);
}
return (
<div>
<h1>Topics</h1>
{user && user.isAdmin ? (
<div>
<h3>Create a New Topic</h3>
<Form
onSubmit={this.createTopicSubmit}
encType="multipart/form-data"
>
<Form.Field>
<input
value={this.state.newTopic}
onChange={e => this.setState({ newTopic: e.target.value })}
placeholder="Create New Topic"
/>
</Form.Field>
<Form.Field>
<label>Upload an Image</label>
<input
type="file"
name="image"
onChange={this.handleSelectedFile}
/>
</Form.Field>
<Button type="submit">Create Topic</Button>
</Form>
</div>
) : null}
<Grid centered>{this.renderTopics()}</Grid>
</div>
);
}
}
const mapStateToProps = state => {
const { loading, topics } = state.topics;
const { user } = state.auth;
return { loading, topics, user };
};
export default requireAuth(
connect(
mapStateToProps,
{ fetchTopics, followTopic, unfollowTopic, createTopic, removeTopic }
)(Topics)
);
TopicActions/createTopic:
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile); //this is still here
// const data = new FormData();
// data.append("image", imageFile);
// data.append("name", topicName);
const data = {
image: imageFile,
name: topicName
};
console.log("DATA TO SEND: ", data); //still shows image file
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
When I send it like this, I receive the following on the back end:
(these are server console.logs)
REQUEST: { image: {}, name: 'NEW TOPIC' }
IMAGE FILE MAYBE? undefined
IMAGE FILES MAYBE? undefined
NO FILE UPLOADED
If I go the new FormData() route, FormData is an empty object, and I get this server error:
POST http://localhost:3090/topics/newTopic net::ERR_EMPTY_RESPONSE
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile);
const data = new FormData();
data.append("image", imageFile);
data.append("name", topicName);
// const data = {
// image: imageFile,
// name: topicName
// };
console.log("DATA TO SEND: ", data); // shows FormData {} (empty object, nothing in it)
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
Solution was to switch to using Firebase instead, and deal with image upload on the React client (this was attempted with cloudinary but with no success). The resulting download url can be saved to the database with the topic name (which is all I wanted from cloudinary) and now it is displaying the correct images along with the topics.

Fetch API - How Do I Send Query From Front End to Backend?

I need to pass a search query from my front end (in React) to my back end (Express) so that my Twitter API route will grab the correct data. Here is where I'm hitting the Twitter API. I was just playing around with a req.query to check out JSON so I know that part needs to be removed.
tweets.js
var express = require('express');
var router = express.Router();
const bodyParser = require('body-parser');
const Twit = require('twit');
const config = require('./config');
var T = new Twit(config);
/* GET users listing. */
router.get('/', function(req, res, next) {
let ticker = req.query.ticker;
T.get('search/tweets', { q: ticker })
.then(function(result) {
var tweets = result.data;
console.log(tweets);
res.send({tweets});
})
.catch(function(err) {
console.log('caught error', err.stack)
res.send({ error: err })
})
})
module.exports = router;
Also note route is set up like this in express
app.use('/tweets', tweets);
And here is my front end in React (ignoring the actual search component for now). Just confused as to how I would send a search query
import React, { Component } from 'react';
import '../App.css';
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch('/tweets?ticker='+query)
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)}
</div>
);
}
}
export default Twitter;
The issue is here in this line.
Wrong one
fetch('/tweets?ticker={query)') <-- this is wrong
Correct one
const filterData = (tweet) => {
return ((!tweet.retweeted) && !(tweet.text.includes('RT #') && ((tweet.in_reply_to_status_id) === null)));
};
class Twitter extends Component {
constructor(props) {
super(props);
this.state = {
tweets:[],
}
}
componentDidMount() {
this.getData("GOOG");
}
getData = (query) => {
fetch(`http://localhost:3001/tweets?ticker=${query}`, {method: 'GET', headers: {"Content-Type": "application/json", "Access-Control-Allow-Origin": "*"}})
.then(res => res.json())
.then(data => data.statuses)
.then(statuses => statuses.filter(filterData))
.then(results => this.setState({tweets:results}))
}
renderTweets(){
if(this.state.tweets){
this.state.tweets.map(tweet =>
<div key={tweet.id}>{tweet.text}</div>
)
}
}
render() {
return (
<div className="App">
<h1>Tweets About </h1>
{this.state.tweets ? this.renderTweets(): ''}
</div>
);
}
}
export default Twitter;
Use template literals
fetch(`/tweets?ticker=${query}`)
OR
use normal string
fetch('/tweets?ticker='+query)
Check here for more details on how template literals works. Your code should be fine now.

Resources