Im a beginner in react, and I was trying to experiment with the useState Hook. I have a react file where I am fetching data from the database in flask which looks like this
import React, {useState, useEffect,useContext } from 'react'
import { Context } from "../../store/appContext"
function Main() {
const {store, actions} = useContext(Context)
const [notes, updateNotes] = useState([])
let arr =[];
const Notes = async () => {
const resp = await fetch('/main/', {
method: "GET",
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Authorization':"Bearer " + store.token
},
});
if(resp.status === 200) {
const data = await resp.json();
arr = data.data
arr.forEach(note => {
updateNotes(...notes, note);
})
console.log("Array data",arr)
updateNotes(...notes, arr);
console.log("state data",notes);
}
}
useEffect(() => {
actions.getTokenAfterRefresh();
Notes();
}, []);
return (
<>
HEy this is thee main page!!!
<div>
Welcome {store.user}!
</div>
<div>
{/* {numNotes == 0 ?
<div>
Create a new note
</div>
:
<div>
Display notes
</div> */}
}
</div>
</>
)
}
export default Main
The flask api looks like this:
from flask import (request, Blueprint, jsonify, g)
from flask_jwt_extended import jwt_required
from flask_jwt_extended import get_jwt_identity
from PIM.db import get_db
bp = Blueprint('main', __name__, url_prefix='/main')
#bp.route('/',methods=('GET', 'POST'))
#jwt_required()
def mainPage():
num=0;
data =[];
if request.method == 'GET':
db = get_db()
user = get_jwt_identity()
if user is not None:
userdata = db.execute('SELECT Info, Title from UserNotes INNER JOIN Users ON Users.Id = UserNotes.user WHERE Users.username = ?',[user]).fetchall()
for row in userdata:
data.append(list(row));
return jsonify({'data': data}), 200
When I try to console log the data in arr variable it displays the data correctly. But when i log the data in the state it doesnt display any data.
Could you please help me solve this issue and also suggest why this is happening.
Thank you
Becasuse setState in asynchronous. State notes only update new value when component re-render. You can check new value of note by using console.log before retrun`
console.log("state data",notes);
return (...)
Related
I'm getting this error when i'm trying to run a react app with an flask api that resolves a pythagoras theorem.
Here is the React code:
import React, { useState } from 'react';
function Pythagoras() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const [c, setC] = useState(0);
const handleSubmit = (e) => {
e.preventDefault();
fetch('http://localhost:5000/api/pythagoras', {
mode: 'no-cors',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({a: a, b: b}),
})
.then(res => res.json())
.then(data => setC(data.c))
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>
A:
<input type="number" value={a} onChange={e => setA(e.target.value)}/>
</label>
<label>
B:
<input type="number" value={b} onChange={e => setB(e.target.value)}/>
</label>
<button type="submit">Calculate</button>
</form>
<h2>C: {c}</h2>
</div>
);
}
export default Pythagoras;
And here is the py code:
`from flask import Flask, jsonify, request
app = Flask(__name__)
#app.route('/api/pythagoras', methods=['POST'])
def pythagoras():
data = request.get_json()
a = data['a']
b = data['b']
c = (a**2 + b**2)**0.5
return jsonify({'c': c})
if __name__ == '__main__':
app.run(debug=True)
Any solution to this?
I tried using the flask-cors packages but it didn't work
Cause
The reason why you are getting this error is due to the 'no-cors' you've specified as your mode. You should remove this mode altogether or you could try again with 'flask-cors':
Flask Cors Retry
from flask import Flask, jsonify, request
from flask_cors import CORS # Import Package
app = Flask(__name__)
CORS(app) # Added this Line
#app.route('/api/pythagoras', methods=['POST'])
def pythagoras():
data = request.get_json()
a = data['a']
b = data['b']
c = (a**2 + b**2)**0.5
return jsonify({'c': c})
if __name__ == '__main__':
app.run(debug=True)
However If that doesn't work for you, you could go an entirely different direction and mess around with proxies in react.
If you chose to go down this route you would have to go inside your package.json file located in the projects root directory and add proxy in the configuration. It should look a little something like this for you:
//package.json
{
"name": "website",
"version": "0.1.0",
"private": true,
"proxy": "http://localhost:5000",
}
with this added to your package.json file you can fetch to "/api/pythagoras"
New Example with proxies set up:
import React, { useState } from 'react';
function Pythagoras() {
const [a, setA] = useState(0);
const [b, setB] = useState(0);
const [c, setC] = useState(0);
const handleSubmit = (e) => {
e.preventDefault();
// Changed Route & Removed 'no-cors'
fetch('/api/pythagoras', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({a: a, b: b}),
})
.then(res => res.json())
.then(data => setC(data.c))
}
return (
<div>
<form onSubmit={handleSubmit}>
<label>
A:
<input type="number" value={a} onChange={e => setA(e.target.value)}/>
</label>
<label>
B:
<input type="number" value={b} onChange={e => setB(e.target.value)}/>
</label>
<button type="submit">Calculate</button>
</form>
<h2>C: {c}</h2>
</div>
);
}
export default Pythagoras;
Keep in mind though that the proxies set up in react will only work in development. In production, you would need to use something like NGINX in order to forward all requests with "/api" to your flask application.
To use nginx and react together is a bit to get used to if you've never used nginx before, but there are a plethora of guides to help you along the way if you ever get lost.
If you need more clarification on any of the tips above then ask away.
from flask import Flask, jsonify, request
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)
#app.route('/receiver', methods = ['POST'])
def postME():
data = request.get_json()
#data = jsonify(data)
print(data.json())
return data
if __name__ == "__main__":
app.run(debug=True)
import React, {useState, useEffect} from 'react';
import logo from './logo.svg';
import './App.css';
function App(){
const [query, setQuery] = useState('');
const updateSearch = e => {
setQuery(e.target.value);
//console.log(query);
}
const [atID, setAtID] = useState('vangyachibhaji');
useEffect(() => {
console.log(query);
}, [atID])
const searchID = e => {
e.preventDefault();
setAtID(query);
console.log("query: " + query);
setQuery('');
postToPython();
}
const postToPython = () => {
fetch("http://127.0.0.1:5000/receiver",
{
method: 'post',
headers: {
'Content-type': 'application/json',
'Accept': 'application/json'
},
// Strigify the payload into JSON:
body:JSON.stringify(query)}).then(res=>{
if(res.ok){
return res.json()
}else{
alert("something is wrong")
}
}).then(jsonResponse=>{
// Log the response data in the console
console.log(jsonResponse)
}
).catch((err) => console.error(err));
}
return(
<div className='Search-bar'>
<form className = 'search-form' onSubmit={searchID}>
<input type="text" placeholder='search' onChange={updateSearch}/>
<button >search</button>
</form>
</div>
);
}
export default App;
I want to send data from a search bar in my webpage (written in React JS) to my python code. The data will be a single simple string, which will be sent to my python code and will be treated as the input.
I tried doing it using Flask but I keep getting an Error 405 (Method Not Allowed). Can someone pls help me out I have been racking my brain for the past 2 days (the stuff after app.run(debug = True) is my React code (.js)
I've got a Django website and I'm trying to integrate Stripe using Django the Stripe API on the backend and Vue.js on the frontend. However, when I try to run the checkout link that's supposed to redirect me to the payment processing page, I get the following error:
Error: IntegrationError: stripe.redirectToCheckout: You must provide one of lineItems, items, or sessionId.
at new r (https://js.stripe.com/v3/:1:6143)
at Js (https://js.stripe.com/v3/:1:165350)
at $s (https://js.stripe.com/v3/:1:165646)
at https://js.stripe.com/v3/:1:166758
at Qs (https://js.stripe.com/v3/:1:166769)
at nc (https://js.stripe.com/v3/:1:167275)
at Ec.redirectToCheckout (https://js.stripe.com/v3/:1:188030)
at http://localhost:8000/dashboard/myaccount/teams/plans/:342:39
Here's the Vue.js method responsible for this:
<script src="https://js.stripe.com/v3/"></script>
<script>
const PlansApp = {
data() {
return {
}
},
delimiters: ['[[', ']]'],
methods: {
subscribe(plan) {
console.log('Subscribe:', plan);
const stripe = Stripe('{{ stripe_pub_key }}');
fetch('/dashboard/myaccount/teams/api/create_checkout_session/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({
'plan': plan
})
})
.then(function(response) {
return response.json()
})
.then(function(session) {
console.log(session)
return stripe.redirectToCheckout({ sessionId: session.sessionId })
})
.then(function(result) {
if (result.error) {
console.log('Error:', result.error.message)
}
})
.catch(function(error) {
console.log('Error:', error);
});
}
}
}
Vue.createApp(PlansApp).mount('#plans-app')
</script>
And here's the Django code that creates the session on the backend:
#login_required
def create_checkout_session(request):
stripe.api_key = settings.STRIPE_SECRET_KEY
data = json.loads(request.body)
plan = data['plan']
if plan == 'basic':
price_id = settings.STRIPE_BASIC_PRICE_ID
else:
price_id = settings.STRIPE_PRO_PRICE_ID
try:
checkout_session = stripe.checkout.Session.create(
client_reference_id = request.user.userprofile.active_team_id,
success_url = '%s%s?session_id={CHECKOUT_SESSION_ID}' % (settings.WEBSITE_URL, reverse('team:plans_thankyou')),
cancel_url = '%s%s' % (settings.WEBSITE_URL, reverse('team:plans')),
payment_method_types = ['card'],
mode = 'subscription',
line_items = [
{
'price': price_id,
'quantity': 1
}
]
)
return JsonResponse({'sessionId': checkout_session['id']})
except Exception as e:
return JsonResponse({'error': str(e)})
I'm struggling to find out why I'm getting the error that I'm getting and would be grateful for any help!
I guest the problem come from the 'success_url' and the 'cancel_url'.
Try to add http:// or https:// in your url
Cordially
I'm trying to write a test for an Ajax get a request in Django.
Here's how I tried.
from django.test import TestCase
from django.urls import reverse
from .models import ResourcePost, User
from register.models import DonorProfile
from django.utils import timezone
class getResourcePostTests(TestCase):
def setUp(self):
ResourcePost.objects.create(
title="test1",
description="test",
quantity=10,
dropoff_time_1=timezone.now(),
dropoff_time_2=timezone.now(),
dropoff_time_3=timezone.now(),
date_created=timezone.now(),
donor=createdonor_1(),
resource_category="FOOD",
status="AVAILABLE",
)
...
def test_getResourcePost(self):
rescource_post_1 = ResourcePost.objects.get(title="test1")
rescource_post_2 = ResourcePost.objects.get(title="test2")
rescource_post_3 = ResourcePost.objects.get(title="test3")
response = self.client.get(reverse('donation:getResourcePosts'))
self.assertEqual(response.status_code, 200)
Here is my view for the ajax call:
#login_required
def getResourcePost(request):
user = request.user
curr_user_rc_1 = user.helpseekerprofile.rc_1
curr_user_rc_2 = user.helpseekerprofile.rc_2
curr_user_rc_3 = user.helpseekerprofile.rc_3
posts = ResourcePost.objects.all()
passingList = []
for post in posts:
if post.date_created >= user.helpseekerprofile.message_timer_before and (
post.resource_category == curr_user_rc_1
or post.resource_category == curr_user_rc_2
or post.resource_category == curr_user_rc_3
):
notiPost = {
"id": post.id,
"title": post.title,
"description": post.description,
}
passingList.append(notiPost)
context = {"resource_posts": passingList}
return JsonResponse(context)
This is my ajax code:
$(document).ready(function () {
setInterval(() => {
$.ajax({
type: 'GET',
url: "{% url 'donation:getResourcePosts' %}",
success: function (response) {
$("#display").html('<i class="fas fa-bell"></i>')
let postCounter = 0
for (i = 0; i < response.resource_posts.length; i++) {
postCounter += 1
}
if (postCounter > 0) {
$("#display").append('<span class="message-number">' + postCounter + '</span>')
}
},
error: function (response) {
console.log("No DATA FOUND")
}
})
}, 1000 * 2)
})
I keep getting fail because the reponse is 302.
Why am I not getting 200? How can I fix this to get 200?
The variables rescource_post_X come up as variables not used. How do I use them? Should I use them?
Thank you!
The #login_required decorator is redirecting your request to the login page
I am not able to redirect to the main.demo. Everything is working fine until data upload after that redirect is not happening. Why?
EDIT: app.py
from flask import Blueprint
main = Blueprint('main', __name__)
import json
import os
from flask import Flask, request,render_template,url_for,redirect
from werkzeug import secure_filename
import glob2
from uuid import uuid4
#main.route('/')
def index():
"""Main index page """
return render_template('index.html')
#main.route('/upload', methods=["GET","POST"])
def upload():
if request.method == 'POST':
form = request.form
# Create a unique "session ID" for this particular batch of uploads.
upload_key = str(uuid4())
# Is the upload using Ajax, or a direct POST by the form?
is_ajax = False
if form.get("__ajax", None) == "true":
is_ajax = True
# Target folder for these uploads.
target = "upload/{}".format(upload_key)
try:
os.mkdir(target)
except:
if is_ajax:
return ajax_response(False, "Couldn't create upload directory: {}".format(target))
else:
return "Couldn't create upload directory: {}".format(target)
print "=== Form Data ==="
for key, value in form.items():
print key, "=>", value
for upload in request.files.getlist("file"):
filename = upload.filename.rsplit("/")[0]
destination = "/".join([target, filename])
print "Accept incoming file:", filename
print "Save it to:", destination
upload.save(destination)
return redirect(url_for("main.demo"))
return render_template("upload.html")
#main.route('/demo',methods=["GET","POST"])
def demo():
if request.method == "GET":
return render_template("demo.html")
def ajax_response(status, msg):
status_code = "ok" if status else "error"
return json.dumps(dict(
status=status_code,
msg=msg,
))
def create_app():
global app
app = Flask(__name__,template_folder=os.path.join(os.path.dirname(os.path.abspath(__file__)),'templates'))
app.debug = True
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.register_blueprint(main)
#app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
return app
Following are some the logs which I am getting:
=== Form Data ===
__ajax => true
Accept incoming file: demo.txt
Save it to: upload/XXXXXXX-XXXXXX-XXXXX-XXXXXXX/demo.txt
"POST /upload HTTP/1.1" 302 231 "http://localhost:5000/upload"
"GET /demo HTTP/1.1" 200 3624 "http://localhost:5000/upload"
It's showing that it's going for demo, but the final url is wrong. Why is it like that?
EDIT 1:
Is it like I am unable to submit the form as the page doesn't gets refreshed ? But its getting redirected to demo() function execute it but doesn't but doesn't render_template. Or it does but somehow gets back to the same function ?
EDIT 2:
More to add this code is using following JavaScript in the background
// Constants
var MAX_UPLOAD_FILE_SIZE = 1024*1024; // 1 MB
var UPLOAD_URL = "/upload";
var NEXT_URL = "/demo";
// List of pending files to handle when the Upload button is finally clicked.
var PENDING_FILES = [];
$(document).ready(function() {
// Set up the drag/drop zone.
initDropbox();
// Set up the handler for the file input box.
$("#file-picker").on("change", function() {
handleFiles(this.files);
});
// Handle the submit button.
$("#upload-button").on("click", function(e) {
// If the user has JS disabled, none of this code is running but the
// file multi-upload input box should still work. In this case they'll
// just POST to the upload endpoint directly. However, with JS we'll do
// the POST using ajax and then redirect them ourself when done.
e.preventDefault();
doUpload();
})
});
function doUpload() {
$("#progress").show();
var $progressBar = $("#progress-bar");
// Gray out the form.
$("#upload-form :input").attr("disabled", "disabled");
// Initialize the progress bar.
$progressBar.css({"width": "0%"});
// Collect the form data.
fd = collectFormData();
// Attach the files.
for (var i = 0, ie = PENDING_FILES.length; i < ie; i++) {
// Collect the other form data.
fd.append("file", PENDING_FILES[i]);
}
// Inform the back-end that we're doing this over ajax.
fd.append("__ajax", "true");
var xhr = $.ajax({
xhr: function() {
var xhrobj = $.ajaxSettings.xhr();
if (xhrobj.upload) {
xhrobj.upload.addEventListener("progress", function(event) {
var percent = 0;
var position = event.loaded || event.position;
var total = event.total;
if (event.lengthComputable) {
percent = Math.ceil(position / total * 100);
}
// Set the progress bar.
$progressBar.css({"width": percent + "%"});
$progressBar.text(percent + "%");
}, false)
}
return xhrobj;
},
url: UPLOAD_URL,
method: "POST",
contentType: false,
processData: false,
cache: false,
data: fd,
success: function(data) {
$progressBar.css({"width": "100%"});
data = JSON.parse(data);
// How'd it go?
if (data.status === "error") {
// Uh-oh.
window.alert(data.msg);
$("#upload-form :input").removeAttr("disabled");
return;
}
else {
// Ok! Get the UUID.
var uuid = data.msg;
//window.location = NEXT_URL + uuid;
window.location = NEXT_URL;
}
},
});
}
function collectFormData() {
// Go through all the form fields and collect their names/values.
var fd = new FormData();
$("#upload-form :input").each(function() {
var $this = $(this);
var name = $this.attr("name");
var type = $this.attr("type") || "";
var value = $this.val();
// No name = no care.
if (name === undefined) {
return;
}
// Skip the file upload box for now.
if (type === "file") {
return;
}
// Checkboxes? Only add their value if they're checked.
if (type === "checkbox" || type === "radio") {
if (!$this.is(":checked")) {
return;
}
}
fd.append(name, value);
});
return fd;
}
function handleFiles(files) {
// Add them to the pending files list.
for (var i = 0, ie = files.length; i < ie; i++) {
PENDING_FILES.push(files[i]);
}
}
function initDropbox() {
var $dropbox = $("#dropbox");
// On drag enter...
$dropbox.on("dragenter", function(e) {
e.stopPropagation();
e.preventDefault();
$(this).addClass("active");
});
// On drag over...
$dropbox.on("dragover", function(e) {
e.stopPropagation();
e.preventDefault();
});
// On drop...
$dropbox.on("drop", function(e) {
e.preventDefault();
$(this).removeClass("active");
// Get the files.
var files = e.originalEvent.dataTransfer.files;
handleFiles(files);
// Update the display to acknowledge the number of pending files.
$dropbox.text(PENDING_FILES.length + " files ready for upload!");
});
// If the files are dropped outside of the drop zone, the browser will
// redirect to show the files in the window. To avoid that we can prevent
// the 'drop' event on the document.
function stopDefault(e) {
e.stopPropagation();
e.preventDefault();
}
$(document).on("dragenter", stopDefault);
$(document).on("dragover", stopDefault);
$(document).on("drop", stopDefault);
}
I have tried to integrate the functionality from the following link: Flask Multiple Upload
Unable to understand that why its still going to /upload even after hitting the /demo
Can somebody help me with this that what is happening in the background ?
In short, my guess is that you are falsely expecting a response to XHR request to change browser's address.
Here's a more detailed explanation of what I think is happening:
A user clicks the upload button
doUpload() Javascript function is called
doUpload() makes an XHR (ajax) request to UPLOAD_URL (POST /upload)
upload() Python function is called and after successful upload generates a "302 Found" redirect response to /demo
browser receives this response and follows the provided URL, in this case it goes to /demo (note that this does not change the address, it happens in the background as part of the XHR request)
demo() Python function renders some HTML, which is returned as a "200 OK" response
the XHR call from step 3 receives this HTML from step 5 and checks whether the response indicates success
success function is triggered and tries to parse HTML as JSON JSON.parse(data), which should fail
I have made some assumptions about your browser, Javascript libraries, HTML templates, and user interaction, but I see this as the most likely explanation from the code you have provided.
My suggestion is to first try replacing return redirect(url_for("main.demo")) with something like this:
if is_ajax:
return ajax_response(True, upload_key)
else:
return redirect(url_for("main.demo"))
In this case, after a successful upload window.location = NEXT_URL will get executed inside your doUpload() and that will change the address in your browser.