How to post data into my React application? - reactjs

I have an issue with my react, I'm working on a MERN template but I can't make my post to work, and I want to be able to add a new blog on my site. When I add a new blog, I seem to get it in my console.log. (the title and the description) but not on my app, I believe it's something with my fetch.
this is my app.js file
import React, {useEffect, useState} from 'react';
import {Router} from "#reach/router";
import Blogs from "./Blogs";
import Blog from "./Blog";
const API_URL = process.env.REACT_APP_API;
function App() {
const [blog, setBlogs] = useState([]);
const [postCount, setPostCount] = useState(0);
useEffect(() => {
async function getData() {
const url = `${API_URL}/blogs`;
const response = await fetch(url);
const data = await response.json();
setBlogs(data);
}
getData();
}, [postCount]);
function getBlog(id) {
const blogObject = blog.find(data => data._id === id);
return blogObject;
}
//callback så min addBlog ved hvor den skal hente data fra
async function addBlog(title, description, date) {
console.log("title", title);
console.log("Description" , description);
const newBlog = {
title: title,
description: description,
date: date
}
const url = `${API_URL}/blogs`;
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json'
},
body: JSON.stringify(newBlog),
});
const data = await response.json();
//setBlogs([...blogs, newBlog]);
setPostCount(postCount + 1); //call my post count that fecths my data automatic
console.log("blog", data);
}
return (
<>
<h1>Blog App!</h1>
<Router>
<Blogs path="/" blogs={blog} addBlog={addBlog}>{blog.id}</Blogs>
<Blog path="/blogs/:id" getBlog={getBlog}></Blog>
</Router>
</>
);
}
export default App;
this is my addBlog.js
import React, { useState } from 'react';
function AddBlog(props) {
//state const for hver properties i din object(question)
const [title, setTitle] = useState("");
const [description, setDescription] = useState("");
const [date, setDate] = useState("");
return (
<><label>Title: </label>
<input type="text" placeholder="Write the title of your Blog" size="30" onChange={(event) => {
setTitle(event.target.value)
}
} /><br /><label>Description: </label>
<input type="text" placeholder="Write the description..." size="30" onChange={(event) => {
setDescription(event.target.value)
}} />
<br />
<button onClick={(event) => props.addBlog(title, description, date)}>Add Question</button>
</>
);
}
export default AddBlog;
I hope someone is able to help me out.
UPDATE here's my screendump of my console - when I press add blog it says POST 401 unAuthrorized.
SORRY IT WAS THE WRONG PROJECT I POSTED AN IMAGE BUT NOW IT'S THE RIGHT PROJECT
Screendump of my console

After looking at your logs, I think you need to send authorization headers alongside your fetch request in order for the back-end to respond.
You could add the authorization header like that - however, you need to find out/generate authorization token that your backend can accept. Also a little improvement, I would make the function a bit more dynamic by allowing it to accept an URL.
useEffect(() => {
async function getData(url) {
const response = await fetch(URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': <your auth token>
}
});
const data = await response.json();
setBlogs(data);
}
getData(`${API_URL}/blogs`);
}, [postCount]);

Related

Twitter user search to display name, followers, following, among others using React.js

I am fairly new to react.js and I'm just trying my hands on a few random projects i can think of and one of them is to make a search engine in react.js that looks up users on twitter by simply entering their name in a search bar and the result will display their details using the Twitter API. However, when doing this i am hit with the follwoing errors in console:
Error ocuring
App.js:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = ({ username }) => {
const [user, setUser] = useState({});
const [tweets, setTweets] = useState({});
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const { data: user } = await axios.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
method : "GET",
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
const { data: tweets } = await axios.get(`https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=${username}&count=200`, {
method : "GET",
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
setUser(user);
setTweets(tweets);
} catch (error) {
setError(error);
}
};
fetchData();
}, [username]);
if (error) {
return <div>An error occurred: {error.message}</div>;
}
return (
<div>
<h1>{user.name}</h1>
<p>Username: {user.screen_name}</p>
<p>Followers: {user.followers_count}</p>
<p>Following: {user.friends_count}</p>
<p>Bio: {user.description}</p>
<p>Date Joined: {user.created_at}</p>
<p>Pinned Tweet: {user.status ? user.status.text : 'No Pinned Tweet'}</p>
<p>Total Tweets: {user.statuses_count}</p>
</div>
);
};
export default App;
UPDATE
I have added the search box feature to the code but I'm still getting the same errors
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const TWITTER_API_URL = 'https://api.twitter.com/1.1/users/search.json';
function App() {
const [username, setUsername] = useState('');
const [userData, setUserData] = useState({});
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
if (searchValue) {
axios
.get(TWITTER_API_URL, {
params: {
q: searchValue,
count: 1
},
headers: {
'Authorization': 'Bearer YOUR_BEARER_TOKEN'
}
})
.then(response => {
setUsername(response.data[0].screen_name);
})
.catch(error => {
console.log(error);
});
}
}, [searchValue]);
useEffect(() => {
if (username) {
axios
.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
headers: {
'Authorization': 'Bearer YOUR_BEARER_TOKEN'
}
})
.then(response => {
setUserData(response.data);
})
.catch(error => {
console.log(error);
});
}
}, [username]);
return (
<div>
<input
type="text"
placeholder="Search by name"
value={searchValue}
onChange={e => setSearchValue(e.target.value)}
/>
{username && (
<div>
<p>Username: {username}</p>
<p>Name: {userData.name}</p>
<p>Following: {userData.friends_count}</p>
<p>Followers: {userData.followers_count}</p>
<p>Bio: {userData.description}</p>
<p>Date Joined: {userData.created_at}</p>
<p>Pinned Tweet: {userData.status.text}</p>
<p>Total Tweets: {userData.statuses_count}</p>
</div>
)}
</div>
);
}
export default App;
I would appreiciate any help given to resolve this issue. Thank you.
I would advise you to move the const fetchData = async () => { ... outside the useEffect() and may sound silly, but for the Authorization: Bearer <YOUR_TOKEN> have you changed the <YOUR_TOKEN> with your actual token? Lastly, you don't need method : "GET" because you are doing axios.get( ...
Please try this code:
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const App = ({ username }) => {
const [user, setUser] = useState({});
const [tweets, setTweets] = useState({});
const [error, setError] = useState(null);
const fetchData = async () => {
try {
const { data: user } = await axios.get(`https://api.twitter.com/1.1/users/show.json?screen_name=${username}`, {
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
const { data: tweets } = await axios.get(`https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=${username}&count=200`, {
headers: {
Authorization: `Bearer <YOUR_TOKEN>`
}
});
setUser(user);
setTweets(tweets);
} catch (error) {
setError(error);
}
};
useEffect(() => {
fetchData();
}, [username]);
if (error) {
return <div>An error occurred: {error.message}</div>;
}
return (
<div>
<h1>{user.name}</h1>
<p>Username: {user.screen_name}</p>
<p>Followers: {user.followers_count}</p>
<p>Following: {user.friends_count}</p>
<p>Bio: {user.description}</p>
<p>Date Joined: {user.created_at}</p>
<p>Pinned Tweet: {user.status ? user.status.text : 'No Pinned Tweet'}</p>
<p>Total Tweets: {user.statuses_count}</p>
</div>
);
};
export default App;
The error message you are seeing is related to CORS (Cross-Origin Resource Sharing) and it is preventing your JavaScript code running on "http://localhost:3000" from making a request to "https://api.twitter.com".
CORS is a security feature implemented by web browsers that prevents a web page from making requests to a different domain than the one that served the web page.
To fix this issue, you will need to set up CORS headers on the server side. The "Access-Control-Allow-Origin" header is used to specify which domains are allowed to make requests to the server. You can set this header to "*" to allow any domain to make requests, or you can set it to the specific domain that your application is running on, "http://localhost:3000" in your case.
You can also use a proxy server in order to avoid CORS issue when trying to access twitter's API. This means that your react application will send the request to your server which will then forward it to twitter's API. It will then receive the response, and forward it back to your react application. This way your application will never be blocked by the CORS policy, as the request is coming from your server and not directly from your application.

Conditional Routing in React based on API calls

So I'm trying to create a React web app with multiple pages and connecting it to Flask to fetch data using the fetch API. Here is what I want to achieve:
If the user submits a Form, React does a POST request to the Flask API which returns a JSON object, which is received by React and I render the predict route. This is handled using the Forms.jsx component, which has the following code:
const Form = () => {
const [title, setTitle] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const movie_submit = {title};
console.log(movie_submit);
fetch('/predict', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(movie_submit)
}).then(() => {
(navigate("/predict"));
})
}
return (
<div className='form_container'>
<form className='form' onSubmit={handleSubmit}>
<input type='text' placeholder='Movie Name' autoFocus
autoComplete='off' value={title} onChange={(e)=>setTitle(e.target.value)}/>
<button className='button'>Recommend!</button>
</form>
</div>
)
}
export default Form
Now I want to perform a GET request to the Flask API to get what should be put into the Predict.js page (/predict route), and the show it.
Predict.js is as:
const Predict = () => {
const [movies, setMovies] = useState([]);
useEffect(() => {
fetch('/predict').then(response =>
response.json().then(data =>
{
setMovies(Object.values(data));
}))
}, []);
const movie_name = movies.map((movie) => <p key={movie.toString()}>{movie}</p>);
return (
<div>
<Navbar />
<h1>Predictions</h1>
<br />
<h2><Movies movie={movie_name}/></h2>
</div>
)
}
export default Predict
But I want this to be such that if the user hasn't submitted the form, then it navigates to /apology route, and if the FLASK API GET request returns an empty object, even then it navigates to /apology route. How do I do this? I understand this is conditional routing of some sort, but I havent been able to quite achieve where I should do this. Here <Movies /> is simply a component that helps in showing the movie names
You can pass a data to the state prop of the location object.
fetch('/predict', {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(movie_submit)
}).then(() => {
(navigate('/predict', { state: { wasFetched: true } }));
})
then in your Predict Component:
const { state } = useLocation();
const { wasFetched } = state;
useEffect(() => {
if (wasFetched) {
// user submited the form
} else {
// user hasn't submited the form
}
}, [wasFetched]);

How to take input in front-end and send request to back-end?

I am working on a basic blog application and it takes title and post input
I am trying to take two inputs and send the request to add them to the database.
The request is made when the user submits the form.
import React, { useState } from "react";
import axios from "axios";
const CreatePost = () => {
const [title, setTitle] = useState("");
const [post, setPost] = useState("");
const onChangeTitle = (e) => {
setTitle(e.target.value);
};
const onChangePost = (e) => {
setPost(e.target.value);
};
const onSubmit = (e) => {
e.preventDefault();
const newPost = {
title: title,
post: post,
};
console.log(newPost);
axios("http://localhost:5000/add", newPost)
.then((res) => console.log(res.data))
.catch((err) => console.log(err));
};
return (
<form onSubmit={onSubmit} className="container">
<div className="mb-3">
<label className="form-label">Title</label>
<input onChange={onChangeTitle} className="form-control"></input>
</div>
<div className="mb-3">
<label className="form-label">Write Post</label>
<textarea
onChange={onChangePost}
className="form-control"
rows="15"
></textarea>
</div>
<button type="submit">POST</button>
</form>
);
};
export default CreatePost;
The post request to add the content is:
app.post("/add", (req, res) => {
const title = req.body.title;
const post = req.body.post;
const newPost = new Post({ title, post });
newPost
.save()
.then(() => res.json("Post added"))
.catch((err) => res.status(400).json("Error:" + err));
});
Error:
GET http://localhost:5000/add 404 (Not Found)
AxiosError {message: 'Request failed with status code 404', name: 'AxiosError', code: 'ERR_BAD_REQUEST', config: {…}, request: XMLHttpRequest, …}
You're sending a GET request:
GET http://localhost:5000/add 404 (Not Found)
But the server-side code is expecting a POST request:
app.post("/add", (req, res) => {
Send a POST request instead:
axios.post("http://localhost:5000/add", newPost)

Using .env.local file for API Credentials to connect to my Airtable DB

This is my very first time using a .env file and I'm in the process of learning Next.js.
I have tried reading the Next.js docs and searching online but I hear they updated Next.js recently and everything changed and I'll admit I am currently way out of my depth.
Here's my .env.local file (changed api details for obvious reasons)
# .env
DB_URL=https://api.airtable.com/v0/
DB_APPID=app1234567890
DB_TABLE=Table%1234567890
DB_KEY=key1234567890
Here's my create.js page:
import { useState } from "react";
import Router, { withRouter } from 'next/router'
export async function getStaticProps() {
const db = await myDB.connect({
AirBaseUrl: process.env.DB_URL,
AirAppId: process.env.DB_APPID,
AirTable: process.env.DB_TABLE,
AirKey: process.env.DB_KEY,
})
}
const Create = () => {
const [title, setTitle] = useState('');
const [body, setBody] = useState('');
const [author, setAuthor] = useState('mario');
const handleSubmit = (e) => {
e.preventDefault();
const blog = { title, body, author };
fetch(AirBaseUrl + AirAppId + "/" + AirTable, {
method: 'POST',
headers: {
'Authorization': "Bearer " + AirKey,
"Content-Type": "application/json"
},
data: {
"records": [
{
"fields": {
"Notes": "Notes posted here",
"Company Name": "Joe Blogs"
}
}
]
},
body: JSON.stringify(blog)
}).then(() => {
// history.go(-1);
Router.push({
pathname: '/'
})
})
}
const getAirtable = (e) => {
e.preventDefault();
const blog = { title, body, author };
fetch(AirBaseUrl + AirAppId + "/" + AirTable, {
method: 'GET',
headers: {
'Authorization': "Bearer " + AirKey,
"Content-Type": "application/json"
},
body: JSON.stringify(blog)
}).then(() => {
// history.go(-1);
Router.push({
pathname: '/'
})
})
}
return (
<div className="create">
<h2>Add a New Blog</h2>
<div>Airtable info to be posted here</div>
<button onClick={getAirtable}>Get Airtable Data</button>
<form onSubmit={handleSubmit}>
<label>Blog title:</label>
<input
type="text"
required
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<label>Blog body:</label>
<textarea
required
value={body}
onChange={(e) => setBody(e.target.value)}
></textarea>
<label>Blog author:</label>
<select
value={author}
onChange={(e) => setAuthor(e.target.value)}
>
<option value="mario">mario</option>
<option value="yoshi">yoshi</option>
</select>
<button>Add Blog</button>
</form>
</div>
);
}
export default Create;
I'm currently getting an error: myDB is not defined
I also can't workout how to use the credentials from .env.local
I have copied this text from the docs:
export async function getStaticProps() {
const db = await myDB.connect({
AirBaseUrl: process.env.DB_URL,
AirAppId: process.env.DB_APPID,
AirTable: process.env.DB_TABLE,
AirKey: process.env.DB_KEY,
})
}
But I'm not sure where myDB should be sense my error.
Finally, you'll notice in the code I have a GET function and a POST function, when you click the button, it should post the GET info in the div but I can't quite workout who I would go about that. The POST function should post details to the Airtable DB when it's click. I've just added dummy info for now.
Can anyone enlighten me as to how I get over the finish line?
In order to surface the environment variables from a .env file, you can use something like dotenv. This allows you to load environment variables from a .env file that you specify.
require('dotenv').config({ path: './.env.local' })
console.log(process.env.FOO)
If your .env is in the root of your project (and just named .env) then you don't need to pass in the path config
require('dotenv').config()

How to send a form input data containing both image and text from React front-end to Express backend using Multer

When I test sending a request containing both image and text grabbbed from user, it comes through to the backend with proper data when I use Postman. Not from React front-end, though. Request does come through but req.body seems to be empty when I console.log it from backend. What am I doing wrong? I am using Multer.
//FRONT-END
import React, { useState } from 'react';
import axios from 'axios';
const ListProperty = (props) => {
const [address, setAddress] = useState('');
const [file, setFile] = useState(null);
const [filename, setFilename] = useState('Choose File');
const handleAddressChange = (evt) => {
setAddress(evt.target.value);
};
const handlePhotoSelect = (evt) => {
setFile(evt.target.files[0]);
setFilename(evt.target.files[0].name);
};
const handleSubmit = async (evt) => {
evt.preventDefault();
const formData = new FormData();
formData.append('address', address);
formData.append('upload', file);
console.log(formData);
try {
axios.post('http://localhost:3000/listproperty', {
headers: { 'Content-Type': 'multipart/form-data' },
body: formData,
});
} catch (err) {
console.log(err);
}
};
return (
<div>
<h2>Property Listing Form</h2>
<span>Provide property address and Photo</span>
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<button>Click to list</button>
</form>
</div>
);
};
export default ListProperty;
//BACK-END
const express = require('express');
const PropertyModel = require('../models/propertyModel');
const router = new express.Router();
const UserModel = require('../models/userModel');
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/images');
},
filename: function (req, file, cb) {
const uniqueName = `${Math.random().toString(32).slice(2)}.jpg`;
req.image = uniqueName;
cb(null, uniqueName);
},
});
const upload = multer({ storage });
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
console.log('hitting Backend router');
const property = new PropertyModel({
...req.body,
owner: req.user._id,
photo: req.image,
});
await UserModel.findByIdAndUpdate(req.user._id, {
$push: { properties: property._id },
});
try {
await property.save();
res.status(200).send(property);
} catch (err) {
console.log(err);
res.status(400).send(err);
}
}
);
module.exports = router;
If you are sending form data in the body you need to use the formidable npm module
you can install it using npm i formidable
then require formidable at top of the file
var formidable = require("formidable");
router.post(
'/listproperty',
upload.single('upload'),
async (req, res) => {
var form = new formidable.IncomingForm();
form.multiples = false;
form.parse(req, async function (err, fields, files) {
/**now here you can get all files in files and fields with fields
in your case you have sent
formData.append('address', address);
formData.append('upload', file);
above two data in form
so you can get your image from files.upload
and address fields.address **/
})
})
In addition, I would suggest you use Axios for api calls
your axios request is not right. axios post request accepts data as a second argument and third argument is for options ( headers etc ),
axios.post('http://localhost:3000/listproperty', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
});
another thing is your request is not being triggered at all. try setting input type to submit instead of using the button to trigger onSubmit handler of the form.
<form onSubmit={handleSubmit}>
<input
type="text"
value={address}
onChange={handleAddressChange}
name={address}
placeholder="Enter address"
/>
<br />
<input type="file" onChange={handlePhotoSelect} />
<input type="submit" value="Submit" />
</form>

Resources