Can't send POST request from react method - reactjs

I am creating a FB like app with Django and react. In the main page there's a feed where users can create posts and post them to the feed.
I am trying to implement the delete post functionality but I am running into some difficulties.
The logic is:
The user clicks on the delete button on a post and the Browser sends and XMLHttpeRequest to the server to delete that post.
Here's the react component:
class Feed extends React.Component {
constructor(props){
super(props);
this.state = {
profile_pic: profile_pic_url,
user: username,
posts: posts_from_server,
}
}
handleClick() {
const text = document.querySelector('#new_post_text').value;
if (text.length > 1) {
const data = {author: username, text: text}
// send that post to the server to save it
const csrftoken = Cookies.get('csrftoken');
const request = new XMLHttpRequest();
request.open('POST', '/create_new_post', true);
request.setRequestHeader('X-CSRFToken', csrftoken);
request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
request.onload = () => {
const response = JSON.parse(request.responseText)
this.setState({
posts : [{author: response.author, author_picture: profile_pic_url, text: response.text, date: response.date}, ...this.state.posts]
})
document.querySelector("#new_post_text").value = '';
console.log(response)
}
request.send(JSON.stringify(data))
}
}
deletePost(post_id, author) {
const post = document.getElementById(post_id)
post.style.animationPlayState = 'running';
setTimeout(() =>{
this.setState({
posts: this.state.posts.filter(post => post.id != post_id)
})
}, 1000)
// delete the post from the server
const data = {'post_author': author, 'id': post_id}
const csrftoken = Cookies.get('csrftoken');
const request = new XMLHttpRequest();
request.open('POST', '/delete_post', true);
request.setRequestHeader('X-CSRFToken', csrftoken);
request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
request.onload = () => {
response = JSON.parse(request.responseText);
console.log(response)
}
request.send(JSON.stringify(data))
}
render() {
return (
<div>
<Post_generator
current_user={this.state.user}
picture={this.state.profile_pic}
onClick={() => this.handleClick()} />
{this.state.posts.map(post => <Post
onClick={() => this.deletePost(post.id, post.author)}
key={post.id}
post_id={post.id}
current_user={this.state.user}
user={post.author}
profile_pic={post.author_picture}
text={post.text}
date={post.date}
/>)}
</div>
)
}
}
this is urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='login'),
path('login', views.login_view, name='login'),
path('index', views.index, name='index'),
path('register', views.register_view, name='register'),
path('logout', views.logout_view, name='logout'),
path('profile', views.profile, name='profile'),
path('change_profile_pic', views.change_profile_pic, name='upload_profile_pic'),
path('create_new_post', views.create_new_post, name='create_new_post'),
path('<str:friend>', views.friends_profile, name='friends_profile'),
path('delete_post', views.delete_post_function, name='delete_post'),
]
and these are the views in views.py handling the two requests:
#login_required
#require_http_methods(['POST'])
def create_new_post(request):
data = json.loads(request.body.decode("utf-8"))
print(data)
new_post = Post(author=request.user, text=data['text'])
new_post.save()
data['date']= new_post.date
return JsonResponse(data)
# delete post
#login_required
#require_http_methods(['POST'])
def delete_post_function(request):
print('sdfgdfsghdsfkgjsdklfg')
response = {"response": "data arrived to server"}
return JsonResponse(response)
The thing that is giving me trouble is the deletePost() method...
When it is executed I get a 405 error in the console.
The funny thing is that the XMLHTTPRequest in the handleClick method works just fine

Ok I figured it out...if the delete_post path in urls.py is not at the end of the urlspatterns list the code works...

Related

An error occurred: IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client_secret string

I have a React app on the frontend, and a django rest API on the back.
I'm trying to get Payment Request Button to work with Apple and Google Pay.
When I test it on the frontend, I get the following message in the browser console:
An error occurred: IntegrationError: Missing value for stripe.confirmCardPayment intent secret: value should be a client_secret string.
I believe there is an issue in my views.py which contains the following:
views.py
class PaymentIntentView(APIView):
def post(self, request, *args, **kwargs):
amount = request.data.get('amount')
currency = request.data.get('currency')
automatic_payment_methods = request.data.get('automatic_payment_methods')
try:
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
automatic_payment_methods={
'enabled': True,
},
# You can also add other options like capture_method, setup_future_usage, etc.
)
return Response({'client_secret': intent.client_secret, 'id': intent.id})
except Exception as e:
return Response({'error': str(e)})
Checkout.js
import React, { useEffect, useState } from 'react';
import {
PaymentRequestButtonElement,
useStripe,
} from '#stripe/react-stripe-js';
import axios from 'axios';
const Checkout = (props) => {
const stripe = useStripe();
const [paymentRequest, setPaymentRequest] = useState(null);
const price = props.price;
const dollar_price = price * 100;
useEffect(() => {
if (stripe) {
const pr = stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Purchase',
amount: dollar_price,
},
requestPayerName: true,
requestPayerEmail: true,
applePay: true,
googlePay: true,
});
pr.canMakePayment().then(result => {
if (result) {
setPaymentRequest(pr);
}
});
}
}, [stripe]);
useEffect(() => {
if (paymentRequest) {
paymentRequest.on('paymentmethod', async event => {
const paymentMethod = event.paymentMethod;
try {
const response = await axios.post(
'/my/django/end-point/create-payment-intent',
{
paymentMethodId: paymentMethod.id,
amount: price,
automatic_payment_methods: {
'enabled': true,
},
currency: 'usd',
}
);
const pi = await stripe.confirmCardPayment(response.data.client_secret, {
payment_method: paymentMethod.id
});
console.log(pi);
if (pi.status === 'succeeded') {
event.complete('success');
console.log('Payment succeeded!');
window.location.href = '/success';
} else if (pi.status === 'requires_action' || pi.status === 'requires_confirmation') {
event.complete('success');
console.log('Additional steps required!');
// Prompt user to complete additional steps
} else if (pi.status === 'requires_payment_method') {
event.complete('fail');
console.log('Payment method required!');
// Prompt user to add a new payment method
} else if (pi.status === 'processing') {
event.complete('success');
console.log('Payment is being processed!');
// Show a message to the user that the payment is being processed
} else if (pi.status === 'canceled') {
event.complete('fail');
console.log('Payment canceled!');
// Show a message to the user that the payment was canceled
} else if (pi.status === 'failed') {
event.complete('fail');
console.log('Payment failed!');
// Show a message to the user that the payment failed
}
} catch (error) {
event.complete('fail');
console.log('An error occurred:', error);
// Show an error message to the user
}
});
}
}, [paymentRequest, stripe]);
if (paymentRequest) {
return <PaymentRequestButtonElement options={{ paymentRequest }} />;
}
return 'Insert your form or button component here.';
};
export default Checkout;
Your code appears to be following the recommended approach for the Payment Request Button using React specified here. The first step in troubleshooting this would be to add some logging to your Django back-end to validate the intent is being created and returned as you expect. You might even want to assign the Response to a variable and log that prior to returning it.
Then I would add a console.log() statement to check the response that is being returned by your axios.post() call. You might try assigning the secret so you can log just that (e.g. const secret = request.data.client_secret).
The last thing I would check is whether the case for the variable is preserved between your front-end and back-end. I'm not an expert on JS Frameworks but I've noticed I will use snake case for property names in my back-end (e.g. nifty_stuff) only to have that transformed to camel case in the front-end (e.g. niftyStuff). Logging the request.data in your front-end should reveal if that is the case.

SSE: Receive JSON from a Flask server through EventSource API

I have a Flask application which responds to a ReactJS application primarily with json. This is important because the Flask app is sending objects from our postgres database.
Recently I started building a "feed" for our website. How I imagine it working: React client issues a GET request to the Flask server for the "items" which will populate the feed. I want to use the EventSource API, using a SSE/stream . This should yield much more fluid page rendering from the user's perspective and allow for much more data to be sent to the client with noticeable loading time (right?).
The problem: EventSource API seems to require a content_type = 'text/event-stream'... which is not ideal for sending objects which much more naturally send as JSON. When I try to send JSON and change the content_type to 'application/json', the browser complains that EventSource was expecting 'text/event-stream'.
My question: is there anything inherent to EventSource which restricts it to 'text/event-stream' responses?
Also! CORS is involved in this process. But I don't think its in the way, because it works for the rest of my app. so this is communication between an app hosted at localhost:5000 and localhost:3000.
Some code:
Flask Server
item_feed.py
#bp.get('/items/stream')
def item_stream():
count = request.args.get("count", None)
def stream_messages_to_client(count):
i = 0
while i < int(count):
data = { "message": "This is a test object."}
# message = "data: This is a test streamable message.\n\n" # This will be received.
message = json.dumps(data) # This will not be received--and this is what I want.
i += 1
yield message
# content_type = 'text/event-stream' # This will be received.
content_type = 'application/json' # This will not be received.
response = Response(stream_with_context(
stream_messages_to_client(count)
), content_type=content_type)
return response
React JS Client
ItemFeed.js
export const ItemFeed = () => {
let [searchParams, setSearchParams] = useSearchParams();
const defaultMessages = [];
const [messages, setMessages] = useState(defaultMessages);
const [isConnectionOpen, setIsConnectionOpen] = useState(false);
const paramsString = searchParams.toString();
const url = process.env.REACT_APP_SERVER + `/items/stream?${paramsString}`;
const eventSource = new EventSource(url, { withCredentials: true })
const handleConnectionOpen = () => {
setIsConnectionOpen(true);
}
const handleStreamedData = (e) => {
// const { message } = JSON.parse(e.data); // This will not parse
const message = e.data; // This will parse, assuming 'data: {{ message }}\n\n'
(messages.length < 10)
? setMessages([...messages, message])
: eventSource.close();
}
const handleConnectionClose = () => {
setIsConnectionOpen(false);
}
eventSource.onopen = handleConnectionOpen;
eventSource.onmessage = handleStreamedData;
eventSource.onclose = handleConnectionClose;
useEffect(() => {
// do something
}, [messages]);
return (
<div className="row">
<p>this is a test</p>
{messages.map((message, index) => (
<div className="col-12" key={message}>{ message }</div>
))}
</div>
);
}

Google Apps Script & React.js : DELETE https://mygoogleappapi.com/exec 405 (Method Not Allowed)

Thank you for reading!
I am learning how to use GAS now,
I can't delete the specific row I selected on google spread sheet.
I got the theme error after trying to delete using "axios.delete method" when I used react app and google script api.
I already passed GET method and POST method using axios. Actually , I could get and post my data from my google spread sheet.
but deleting could not access well.
I found this error 405 is not allowed to access my google sheet, but Why can I get this error even though the post method was accessible?
My App script or My react.js code need to have any code else ? I can't solve this problem...
I want to solve this error and delete the specific row I selected. Also, I want to know a workaround for this error.
Do you have any idea ? If you have some good idea,Could you tell me please ?
Thank you for reading.
this is my App script code.
function doDelete(req, sheet) {
var id = req.parameter.id;
var Row = sheet.getLastRow();
for (var i = 1; i <= Row; i++) {
var idTemp = sheet.getRange(i, 1).getValue();
if (idTemp == id) {
sheet.deleteRow(i);
}
}
}
this is my reactjs code.
import React,{ useState , Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
var optionAxios = {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin':'*' ,
}
}
const api = 'https://mygoogleappscriptapi.com/exec';
class Price extends Component {
constructor(){
super();
this.state = {
info: []
};
this.getInfo();
this.createInfo = this.createInfo.bind(this);
this.deleteInfo = this.deleteInfo.bind(this);
};
// accessed get!
getInfo = () =>{
axios.get(api)
.then((res) =>{
console.log(res.data)
this.setState({
info: res.data
})
})
}
// accessed post!
createInfo = () =>{
axios.post(api,{
product: "hoge",
price: 1000,
miniLot: 1000,
cartonSize: "40*30*50"
},optionAxios)
.then((res) => {
this.getInfo(res);
})
}
// cant't access delete!
deleteInfo = (e) => {
console.log(e);
axios.delete(api,{
id: e,
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
render(){
return (
<div className={this.root}>
<Grid container>
<Grid item xs={11}>
<button onClick={this.createInfo}>createButon</button>
<Paper>
{this.state.info.map(info => <div key={info.id}>
{info.product}
<button onClick={() => this.deleteInfo(info.id)}>×</button>
</div>)}
</Paper>
</Grid>
</Grid>
</div>
);
}
}
export default Price;
Only the following HTTP methods are supported:
POST
GET
DELETE method is not supported by google-apps-script-web-application.
You can use post:
Server side:
function doPost(e){
if(e.parameter.option === "DELETE") return doDelete(e);
/*rest of doPost here*/
}
React:
// convert to axios.post
deleteInfo = (e) => {
console.log(e);
axios.post(api,{//modified
id: e,
option: "DELETE",//added
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
Try this:
function doDelete(req, sh) {
var id = req.parameter.id;
const ss=SpreadsheetApp.getActive();
sh=sh||ss.getActiveSheet();
var vs=sh.getRange(1,1,sh.getLastRow(),1).getValues();
var d=0;
for (var i=0;i<vs.length;i++) {
if (vs[i][0]== id) {
sh.deleteRow(i+1-d++);
}
}
}

How to properly paginate data in React with Firestore?

As I am starting my experience with Firebase I am a little bit struggling with the pagination of posts on my blog website :(.
I think I kind of understood the docs from Google and I know how to move the pagination to the next page. However, I have absolutely no clue how to paginate back to the previous page.
Basically, I wanted to have a simple pagination component which will look something like that: < 1 2 3 [...] > (where you can paginate next and back using the arrows).
It is fine to paginate to the next page but, when it comes to paginating back I cannot find any proper tutorial to do it in pure React.
I have tried to use various methods from startAt, endAt, endBefore etc. But the result was or an error or it was moving me back to the first page (even when I was on the third or fourth)
I even tried to find the first object in an array and use it as endBefore but it resulted again in paginating back to the first page.
That's how my code looks right now (yes I know that pageNext() and pagePrev() are the same)
import React, { Component } from 'react'
import { withFirebase } from './Firebase'
import Post from './Post'
import '../scss/Post.scss'
class Posts extends Component {
constructor(props) {
super(props);
this.state = {
loading:false,
posts:[],
post_id:[],
lastVisible:null,
limit:2
}
this.handlePageNext = this.handlePageNext.bind(this);
}
componentDidMount() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.limit(2)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible: lastVisible});
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
handlePageNext() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.startAt(this.state.lastVisible)
.limit(this.state.limit)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible:lastVisible });
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
handlePagePrev() {
let newPosts=[];
let postsId=[];
this.setState({ loading: true });
this.props.firebase.posts()
.orderBy('date', 'desc')
.startAt(this.state.lastVisible)
.limit(this.state.limit)
.get().then(querySnapshot => {
let lastVisible = querySnapshot.docs[querySnapshot.docs.length-1];
this.setState({ lastVisible:lastVisible});
querySnapshot.forEach(doc => {
newPosts = newPosts.concat(doc.data());
postsId = postsId.concat(doc.id);
this.setState({
posts:newPosts,
post_id:postsId,
loading:false
});
})
})
}
render() {
return (
<div className='posts'>
<div className='row'>
{this.state.posts.map((post, i) => (
<Post
key={i}
title={post.title}
author={post.author}
desc={post.desc}
text={post.text}
id={this.state.post_id[i]}
date={post.date}
imgURL={post.imgURL}/>
))}
{this.state.loading && <p>Loading...</p>}
<button className='btn' onClick={() => this.handlePagePrev()}>←</button>
<button className='btn' onClick={() => this.handlePageNext()}>></button>
</div>
</div>
)
}
}
export default withFirebase(Posts);
I wanted to have a simple pagination using buttons (left and right arrows) but I am struggling with it for already 3rd hour and cannot find the proper solution to this.
You have to keep the "lastVisible" and pass it to startAfter(). 2 functions I wrote below:
export const getMostRecentPostsFirstPage = (limit, specificUserId) => {
if (!Number.isInteger(limit) || limit < 1) {
throw new Error('limit must be a positive integer');
}
const collection = Firestore.collection('posts');
let query = null;
if (specificUserId) {
query = collection
.where('userId', '==', `${specificUserId}`)
.orderBy('postedTimestamp', 'desc')
.limit(limit);
} else {
query = collection.orderBy('postedTimestamp', 'desc').limit(limit);
}
return new Promise((resolve, reject) => {
const posts = [];
query
.get()
.then(snapshot => {
const lastVisible = snapshot.docs[snapshot.docs.length - 1];
snapshot.forEach(post => {
posts.push(post.data());
});
const hasMore = posts.length == limit;
resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
})
.catch(error => reject(error));
});
};
export const getMostRecentPostsNextPage = (lastVisible, limit, specificUserId) => {
if (!lastVisible) {
throw new Error('Need to provide lastVisible argument');
}
if (!Number.isInteger(limit) || limit < 1) {
throw new Error('limit must be a positive integer');
}
const collection = Firestore.collection('posts');
let query = null;
if (specificUserId) {
query = collection
.where('userId', '==', `${specificUserId}`)
.orderBy('postedTimestamp', 'desc')
.startAfter(lastVisible)
.limit(limit);
} else {
query = collection
.orderBy('postedTimestamp', 'desc')
.startAfter(lastVisible)
.limit(limit);
}
return new Promise((resolve, reject) => {
const posts = [];
query
.get()
.then(snapshot => {
const lastVisible = snapshot.docs[snapshot.docs.length - 1];
snapshot.forEach(post => {
posts.push(post.data());
});
const hasMore = posts.length == limit;
resolve({ posts: posts, lastVisible: lastVisible, hasMore: hasMore });
})
.catch(error => reject(error));
});
};
It uses redux-saga, but you get the idea.
on first query, do not call "startAfter()", but do on the subsequent queries, and you must save "lastVisible" between each call.
Here is standard pagination by using Firebase in reactjs.

multiple image upload one by one with delete and change option with preview

I am trying to upload image one by one with change and delete option(for each image uploaded) in multiple view with react, apollo client. But with this I can't get the clear thought about how to perform this easily and confused a lot..
Please anyone help me to get rid of this...
**updated**
Hi now i am using react-dropzonecomponent so far, but here i did mutiple file upload with delete option only..
Here i can send the files to server(node using mulitpart form data), in DB create the file in server end and store the path in database with path name only... But here i can't show the image files in front end from the path got from Back end...
const initialState = {
files: [],
imagePreviewUrl: []
};
class Image extends React.Component {
constructor(props) {
super(props);
this.state = { ...initialState };
}
componentWillMount() {
let {match, data} = this.props;
const id = match.params.id && match.params.id.slice(1);
if (id) {
let currentProduct = (data && data.getProduct) && data.getProduct.find((data) => {
return data.id == id;
});
this.setState({
imagePreviewUrl: currentProduct.images
});
}
}
handleAdd(file) {
console.log(file)
var allFiles = this.state.files;
allFiles = allFiles.concat([file]);
this.setState({
files: allFiles
});
}
handleRemove(file) {
let allFiles = this.state.files;
this.state.files.forEach((itr, i) => {
if (itr.upload.uuid == file.upload.uuid) {
allFiles.splice(i, 1)
}
});
this.setState({
files: allFiles
});
console.log(this.state.files, allFiles, file)
}
render() {
let {match, classes, data} = this.props;
let {imagePreviewUrl} = this.state;
const id = match.params.id && match.params.id.slice(1);
var self = this;
return (
<GridContainer>
<DropzoneComponent
config={{
postUrl: 'no-url',
iconFiletypes: ['.jpg', '.png', '.gif'],
showFiletypeIcon: true
}}
eventHandlers=
{{
addedfile: (file) => this.handleAdd(file),
removedfile: (file) => this.handleRemove(file),
init: (dropzone) => {
console.log(dropzone)
}
}}
djsConfig={{
autoProcessQueue: false,
addRemoveLinks: true,
previewTemplate: ReactDOMServer.renderToStaticMarkup(
...<img data-dz-thumbnail="true" /> ...)}} />
</GridContainer>
);
}
}
export default withStyles(style)(Image);

Resources