I'm trying to upload a file to flask with fetch. What I do is onChange put the file in a useState hook and then onSubmit append it to FormData. After I send the file to my flask api with fetch. I put in a response to show me if the file is there or not and it keeps coming back as a blank dict.
Here is the react code.
import React, {useState} from 'react'
function App() {
const [file, setFile] = useState(null);
const fileInserted = (event) => setFile(event.target.value);
const handleSubmit = async (event) => {
event.preventDefault();
if (file) {
const data = new FormData();
data.append("file", file);
const resp = await fetch("/run", {
method: "POST",
body: data,
}).then(res => res.json())
.then(data => console.log(data.file))
.catch(error => console.error(error))
}
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="file"
onChange={fileInserted}
/>
<button type="submit" disabled={!file}>Submit</button>
</form>
</div>
);
}
export default App;
Here is the flask code.
#app.route("/run", methods=['GET', 'POST'])
def index():
if request.method == "POST":
if 'file' not in request.files:
return {"file": request.files}
file = request.files['file']
return {"file": file}
else:
return {"file": "this a get"}
The FormData you're populating gets recreated as an empty object on every render.
You also don't need to use an effect hook to run the fetch...
function App() {
const [file, setFile] = useState(null);
const fileInserted = (event) => setFile(event.target.value);
const handleSubmit = async (event) => {
if (file) {
const data = new FormData();
data.append("file", file);
const resp = await fetch("/run", {
method: "POST",
body: data,
});
// TODO: handle error/success
}
event.preventDefault();
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="file"
value={file}
onChange={fileInserted}
/>
<button type="submit" disabled={!file}>Submit</button>
</form>
</div>
);
}
export default App;
Related
I'm new to React, and I'm trying to make a recpie app with react, right know I want to save the data in json file from the add form. so I can save the data but when I want to redirect the user to the home page using useEffict with navigate. I can't go to the create page when adding navigate to the useEffict.
Create file code:
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useFetch } from "../../hooks/useFetch";
// Styles
import "./Create.css";
export default function Create() {
const [title, setTitle] = useState("");
const [method, setMethod] = useState("");
const [cookingTime, setCookingTime] = useState("");
const [newIngredient, setNewIngredient] = useState("");
const [ingredients, setIngredients] = useState([]);
const { postData, data } = useFetch("http://localhost:3000/recipes", "POST");
const ingredientsInput = useRef(null);
const navigate = useNavigate();
// Methods
const handleSubmit = (e) => {
e.preventDefault();
postData({
title,
ingredients,
method,
cookingTime: cookingTime + " minutes",
});
};
const handleAdd = (e) => {
e.preventDefault();
const ing = newIngredient.trim();
if (ing && !ingredients.includes(ing)) {
setIngredients((preIng) => [...preIng, ing]);
}
setNewIngredient("");
ingredientsInput.current.focus();
};
useEffect(() => {
if (data) {
navigate("/");
console.log(data);
}
}, [data, navigate]);
return (
<div className="create">
<form onSubmit={handleSubmit}>
<label>
<span>Recipe Title:</span>
<input
type="text"
onChange={(e) => setTitle(e.target.value)}
value={title}
required
/>
</label>
<label>
<span>Recipe ingredients:</span>
<div className="ingredients">
<input
type="text"
onChange={(e) => setNewIngredient(e.target.value)}
value={newIngredient}
ref={ingredientsInput}
/>
<button onClick={handleAdd} className="btn">
Add
</button>
</div>
</label>
{ingredients.length > -1 && (
<p>
Current ingredients:{" "}
{ingredients.map((ing) => (
<span key={ing}>{ing}, </span>
))}
</p>
)}
<label>
<span>Recipe Method:</span>
<textarea
onChange={(e) => setMethod(e.target.value)}
value={method}
required
/>
</label>
<label>
<span>Recipe Time (minutes):</span>
<input
type="number"
onChange={(e) => setCookingTime(e.target.value)}
value={cookingTime}
required
/>
</label>
<button className="btn">Submit</button>
</form>
</div>
);
}
useFetch file code:
import { useState, useEffect } from "react";
export const useFetch = (url, method = "GET") => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [option, setOption] = useState(null);
const postData = (data) => {
setOption({
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
};
useEffect(() => {
const controller = new AbortController();
const fetchData = async (fetchOption) => {
setIsPending(true);
try {
const res = await fetch(url, {
...fetchOption,
signal: controller.signal,
});
if (!res.ok) {
throw new Error(res.statusText);
}
const data = await res.json();
setIsPending(false);
setData(data);
setError(null);
} catch (err) {
if (err.name === "AbortError") {
console.log("the fetch was aborted");
} else {
setIsPending(false);
setError("Could not fetch the data");
}
}
};
if (method === "GET") {
fetchData();
}
if (method === "POST") {
fetchData(option);
}
return () => {
controller.abort();
};
}, [url, option, method]);
return { data, isPending, error, postData };
};
I don't know from where the issue came.
The problem was from useFetch file. when I want to do a post request I shoud cheack if the option useState has a value.
Before I was just check if there is a post method:
const [option, setOptions] = useState(null);
if (method === "POST") {
fetchData(option);
}
Know I'm checking if there is a value in option
const [option, setOptions] = useState(null);
if (method === "POST" && option) {
fetchData(option);
}
You basically trying to add a variable that is not a react state variable into the useEffect on update
const [recipes, setReceipies] = useState();
useEffect(async ()=> { const {data} = awawit useFetch("http://localhost:3000/recipes", "POST")
setReceipies(data);
},[])
navigate("/");
},[recipes]);
Or ofc you can navigate all the way from the mounting useEffect
Good Luck
after you save the data, simply add this code
const history = createBrowserHistory()
history.push(`/`)
I have big apps, that use history, and I never had a problem with it.
and I recomend you to use SWR for data-fetching - React Hooks for Data Fetching.
very simple and powerfull tool:
https://swr.vercel.app/
I am trying to fetch data from my Express api which is working, but there is issue in the frontend,
it seems like when I change the input state there is a delay even if I call the functions the fetch data after updating the input state.
Here is my component:
import React, { useState, useEffect } from 'react';
import UsersList from './UsersList.js';
function SearchBox() {
const [input, setInput] = useState("");
const [githubUserResult, setGithubUserResult] = useState([]);
const [gitlabUserResult, setGitlabUserResult] = useState([]);
const [isLoaded, setIsloaded] = useState(false);
const [error, setError] = useState(null);
const handleInputChange = (e) => {
setInput(e.target.value);
}
const searchUser = async (e) => {
e.preventDefault();
searchUserOnGithub(input);
searchUserOnGitLab(input);
setIsloaded(true);
}
const searchUserOnGithub = async (username) => {
await fetch(`/api/github/userinfo/${username}`, {
method: "GET", headers: {
"Content-Type": "application/json",
}
})
.then(res => res.json())
.then(
(result) => {
setGithubUserResult(result);
console.log(githubUserResult);
},
(error) => {
setError(error)
})
}
const searchUserOnGitLab = async (username) => {
await fetch(`/api/gitlab/userinfo/${username}`, {
method: "GET", headers: {
"Content-Type": "application/json",
}
})
.then(res => res.json())
.then(
(result) => {
setGitlabUserResult(result);
console.log(gitlabUserResult);
},
(error) => {
setError(error)
})
}
if (error) {
return <div>Error: {error.message}</div>;
}return (
<div className='search-container'>
<form>
<input type="text" onChange={handleInputChange} />
<button type="button" onClick={searchUser} >Search</button>
</form>
<h3>github</h3><br />
{/*isLoaded ? <UsersList users={githubUserResult} />: ''*/}
<h3>gitLab</h3><br />
{/*isLoaded ? <UsersList users={gitlabUserResult} /> : ''*/}
</div>
)
}
export default SearchBox;
On the console we can see the first attempt failling(first click), and the second one working:
You are logging your states, and your states will not update until the the next render. When you click on the button the following code is executed:
setGithubUserResult(result);
console.log(githubUserResult);
See Closures.
result is the value returned from your request, but githubUserResult has the initial value you declared for the state, which is an empty array. Your state (githubUserResult) does not update synchronously and immediately when you set the state. Hence the stale value of the state.
If you want to access the updated value, one way would be to use the result not the state directly.
Well my problem its that always that i send the login info to the backend, react return me Cannot Post /login. The problem its that i have a res.redirect to the main page but it dosent work and i dont know if its an error of Axios or the controller, because the information arrives correctly. What do you think? What issue im having?
React Login
import React, {Component} from "react";
import "./user.css"
import Api from "../Instrumentos/apiInstrumentos"
import { withRouter } from "react-router";
class User extends Component {
constructor(props){
super(props);
this.state = {
user: []
}
};
async componentDidMount(){
try{
console.log()
const id = this.props.match.params.id;
let user = await fetch(`http://localhost:5000/user/${id}`).then(response =>
response.json())
this.setState({
user: user
});
console.log(this.state.user)
}
catch(error){
console.log(error);
}
}
render(){
let user = this.state.user
return (
<section id="user-detail">
<section id="user_saved">
<article>
<figure id="user_figure">
<img src={`http://localhost:5000${user.photo}`} />
</figure>
<h3 id="bienvenido_user">Bienvenido {user.nombre}</h3>
<form className="margin-sections">
<fieldset>
<input type="text" name="" placeholder={user.nombre}
className="input-profile"></input>
</fieldset>
<fieldset>
<input type="text" name="apellido" placeholder={user.apellido}
className="input-profile"></input>
</fieldset>
<fieldset>
<input type="password" name="password" placeholder={user.password}
className="input-profile"></input>
</fieldset>
<fieldset>
<button className="button-login button-detail"
type="submit">Enviar</button>
</fieldset>
</form>
</article>
<article>
<h3 id="Instrumentos_guardados">Instrumentos Guardados</h3>
<Api />
</article>
</section>
</section>
);
}
}
useForm Hook
import {useState} from 'react';
import Axios from 'axios';
export const useForm = (initialForm, validateForm) => {
const [form, setForm] = useState(initialForm);
const [errors, setErrors] = useState({});
const [loading,] = useState(false);
const [response,] = useState(null);
const handleChange = (e) => {
const { name,value } = e.target;
setForm({
...form,
[name]: value
});
};
const handleBlur = (e) => {
handleChange(e);
setErrors(validateForm(form));
};
const handleSubmit = (e) => {
setErrors(validateForm(form));
Axios
.post('http://localhost:5000/usuarios/login', form)
.then(response => {
console.log(response)
})
.then(data => console.log(data))
.catch(error => {
console.log(error)
})
};
return {
form,
errors,
loading,
response,
handleChange,
handleBlur,
handleSubmit
};
}
Backend Controller
const main = {
acceso: async (req, res) => {
console.log(req.body)
return res.redirect("http://localhost:3000")
}
}
Backend Routes
const express = require('express');
const router = express.Router();
const path = require("path");
const fs = require("fs");
const user = require("../controllers/user");
const multer = require("multer");
// ************ Multer ************
const dest = multer.diskStorage({
destination: function (req, file, cb) {
let dir = path.resolve(__dirname,"../../public/uploads","users",
String(req.body.nombre).trim().replace(/\s+/g, ''))
if (!fs.existsSync(dir)){
fs.mkdirSync(dir);
}
cb(null, dir)
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now()+ path.extname(file.originalname))
}
})
const upload = multer({storage:dest});
// ************ Routes ************
router.post("/login", user.acceso)
router.post('/guardar', upload.single("file") , user.post)
module.exports = router;
/*** Entry Point ***/
Entry point Route definition
const users = require("./routes/user");
app.use("/usuarios", users);
/*** Console.log ***/
Console.log Response { email: 'juansepincha#gmail.com', password: '12345' }
From now thanks for all and have a great weekend!
I am trying to get input from the user in a form field and access the data in my backend server.js . I wanted to use this data in order to pass parameters to the Yelp Fusion API I am using. I know I can use axios for this but am unsure how to accomplish this at this time.
Here is my server.js:
const express = require('express')
const dotenv = require('dotenv').config()
const port = process.env.PORT || 5000
var axios = require('axios');
const app = express()
var cors = require('cors');
app.use(cors());
// app.get('/', (req, res) => {
// var config = {
// method: 'get',
// url: 'https://api.yelp.com/v3/businesses/search?location=Houston',
// headers: {
// 'Authorization': 'Bearer <API_KEY>
// }
// };
// axios(config)
// .then(function (response) {
// //const data = JSON.stringify(response.data);
// res.json(response.data)
// })
// .catch(function (error) {
// console.log(error);
// });
// })
app.listen(port, () => console.log(`Server started on port ${port}`))
Here is the App.js in which I need to pass the state from the input field to the backend:
import React,{useEffect,useState} from 'react';
import './App.css';
import axios from 'axios'
function App() {
const [zip,setZip] = useState("")
function handleSubmit() {
useEffect(() => {
axios.post("http://localhost:8000")
.then(res => {
console.log(res)
})
})
}
// const [results, setResults] = useState({})
// console.log(results)
// useEffect(() => {
// axios.get('http://localhost:8000').then(res => {
// console.log(res)
// })
// }, [])
return (
<div>
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;
Ok, here's what's wrong with your code:
hooks can only be placed on the first level of functional component (outside any sub-functions) and must be before any return statements.
Use effect will fire on render, in your code it looks like you wanna trigger it on event click.
I would do it this way if I were you:
function App() {
const [zip,setZip] = useState("");
const triggerAPI = useCallback(async () => {
// Use async await instead of chained promise
const res = await axios.post("http://localhost:8000", { zip: zip });
console.log(res)
}, [zip]);
const handleSubmit = useCallback((e) => {
e.preventDefault()
triggerAPI();
}, [triggerAPI])
const handleChange = useCallback((event) => {
setZip(event.target.value);
}, []);
return (
<div>
<form onSubmit={handleSubmit}>
<label>
ZIP:
<input type="text" value={zip} name="zip" onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;
changes:
I used useCallback to memoize the functions
I used async/await instead of chained promise (looks cleaner)
You use useEffect() function wrongly.
Handle the input element state properly.
To send data to the server you can use axios.post(url,data).then()
import React, { useState } from "react";
import axios from "axios";
function App() {
const [zip, setZip] = useState("");
function handleSubmit(event) {
event.preventDefault();
axios.post("http://localhost:8000", { zip: zip }).then((res) => {
console.log(res);
});
}
const handleChange = (event) => {
setZip(event.target.value);
};
return (
<div>
<form onSubmit={handleSubmit}>
<label>
ZIP:
<input type="text" value={zip} name="zip" onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
export default App;
I'm using "react-dropzone" for dropping files :
import React from "react";
import { useDropzone } from "react-dropzone";
const DropzoneUpload = ({ onDrop, accept }) => {
// Initializing useDropzone hooks with options
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept
});
return (
<div {...getRootProps()}>
<input className="dropzone-input" {...getInputProps()} />
<div className="text-center">
{isDragActive ? (
<p className="dropzone-content">Release to drop the files here</p>
) : (
<p className="dropzone-content">
Drag 'n' drop some files here, or click to select files
</p>
)}
</div>
</div>
);
};
export default DropzoneUpload;
And it's used in App.js like this :
<DropzoneUpload
onDrop={onDrop}
accept={
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
}
/>
Where the onDrop function is :
const onDrop = useCallback(async acceptedFiles => {
// this callback will be called after files get dropped, we will get the acceptedFiles. If you want, you can even access the rejected files too
console.log(acceptedFiles);
const axiosInstance = axios.create({
baseURL: "http://localhost:5000"
});
const reader = new FileReader();
reader.onabort = () => console.log("file reading was aborted");
reader.onerror = () => console.log("file reading has failed");
reader.onload = () => {
// Do whatever you want with the file contents
const binaryStr = reader.result;
const body = JSON.stringify({
binaryStr
});
await axiosInstance.post("/api/upload", body);
};
acceptedFiles.forEach(file => reader.readAsArrayBuffer(file));
}, []);
The problem : when I add Async Await to the callback function onDrop I get :
Parsing error: Can not use keyword 'await' outside an Async function
So how can I wait to the answer from the server ?
// you forgot add async keyword here
reader.onload = async () => {
const binaryStr = reader.result;
const body = JSON.stringify({
binaryStr
});
await axiosInstance.post("/api/upload", body);
};
Also you can delete top level async at useCallback(async....