Stripe payment method. Use card [split-form] not working. Can't get card number expiry and cvc number - node.js

I am implementing stripe with react and node.
but I am not getting these values
<CardNumberElement/>
<CardExpiryElement/>
<CardCvcElement/>
Without its value can't get token and can't charge money.
if I only use cardElement that's work but cardElement is a one-line input field but I want to split it. so that's why I used CardNumberElement , CardExpiryElement, and CardCvcElement for splitting.
backend code is perfect but the error is inside frontend because we can't pass values perfectly for creating the stripe token for the payment.
Frontend Code:
import React from "react";
import { loadStripe } from "#stripe/stripe-js";
import {
Elements,
CardElement,
useStripe,
useElements,
CardNumberElement,
CardExpiryElement,
CardCVCElement,
injectStripe,
StripeProvider,
CardCvcElement
} from "#stripe/react-stripe-js";
import axios from "axios";
import { ServiceBooking } from "../../services/service-booking"
const CheckoutForm = ({ success }) => {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async event => {
event.preventDefault();
let number = elements.getElement(CardNumberElement);
let cvc = elements.getElement(CardCvcElement);
console.log("farrukh",number)
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: {
number: number,
exp_month: 4,
exp_year: 2021,
cvc: cvc,
}
});
if (!error) {
const { id } = paymentMethod;
try {
const data = await ServiceBooking.charge(id, 1099);
console.log(data);
success();
} catch (error) {
console.log(error);
}
}
};
return (
<form
onSubmit={handleSubmit}
style={{ maxWidth: "400px", margin: "0 auto" }}
>
<h2>Price: $10.99 USD</h2>
<CardNumberElement/>
<CardExpiryElement/>
<CardCvcElement/>
<button type="submit" disabled={!stripe}>
Pay
</button>
</form>
);
};
// you should use env variables here to not commit this
// but it is a public key anyway, so not as sensitive
const stripePromise = loadStripe("pk_test_wSvr6guTJvkKmv21jVqVd2D20049BVPKHP");
const Checkout = () => {
const [status, setStatus] = React.useState("ready");
if (status === "success") {
return <div>Congrats on your empanadas!</div>;
}
return (
<Elements stripe={stripePromise}>
<CheckoutForm
success={() => {
setStatus("success");
}}
/>
</Elements>
);
};
export default Checkout;
Backend Code:
router.post(
"/charge",
asyncHandler(async function (req, res) {
const { id, amount } = req.body;
try {
const payment = await stripe.paymentIntents.create({
amount,
currency: "USD",
payment_method: id,
confirm: true
});
console.log(payment);
return res.status(200).json({
confirm: "abc123"
});
} catch (error) {
console.log(error);
return res.status(400).json({
message: error.message
});
}
})
);
I have tried but could not charge (payment) successfully

in Stripe.js, you would just need to pass the CardElement to the stripe.createPaymentMethod call. You do not need the card number nor you can get the card number due to security reason.
let number = elements.getElement(CardNumberElement);
...
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: number
});
You may ask how do I pass in the cvc and expiry date? The answer is that you don't have to, Stripe.js will automatically locate the CVC and expiry input automatically in the same page.
See reference at https://stripe.com/docs/stripe-js/react

You need to change your front-end code. For a split form to work, you should use three different (card) keys under .createPaymentMethod to catch the CardNumberElement, CardExpiryElement, and CardCvcElement respectively
.createPaymentMethod(
{
type: 'card',
card: elements.getElement(CardNumberElement),
card: elements.getElement(CardExpiryElement),
card: elements.getElement(CardCvcElement),
}
Your Frontend Code should look like this:
import React from "react";
import { loadStripe } from "#stripe/stripe-js";
import {
Elements,
CardElement,
useStripe,
useElements,
CardNumberElement,
CardExpiryElement,
CardCVCElement,
injectStripe,
StripeProvider,
CardCvcElement
} from "#stripe/react-stripe-js";
import axios from "axios";
import { ServiceBooking } from "../../services/service-booking"
const CheckoutForm = ({ success }) => {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async event => {
event.preventDefault();
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: 'card',
card: elements.getElement(CardNumberElement),
card: elements.getElement(CardExpiryElement),
card: elements.getElement(CardCvcElement),
});
if (!error) {
const { id } = paymentMethod;
try {
const data = await ServiceBooking.charge(id, 1099);
console.log(data);
success();
} catch (error) {
console.log(error);
}
}
};
return (
<form
onSubmit={handleSubmit}
style={{ maxWidth: "400px", margin: "0 auto" }}
>
<h2>Price: $10.99 USD</h2>
<CardNumberElement/>
<CardExpiryElement/>
<CardCvcElement/>
<button type="submit" disabled={!stripe}>
Pay
</button>
</form>
);
};
// you should use env variables here to not commit this
// but it is a public key anyway, so not as sensitive
const stripePromise = loadStripe("pk_test_wSvr6guTJvkKmv21jVqVd2D20049BVPKHP");
const Checkout = () => {
const [status, setStatus] = React.useState("ready");
if (status === "success") {
return <div>Congrats on your empanadas!</div>;
}
return (
<Elements stripe={stripePromise}>
<CheckoutForm
success={() => {
setStatus("success");
}}
/>
</Elements>
);
};
export default Checkout;

Just for anyone coming up with this issue in 2022. I was having a similar set up as OP and the answer is here on YouTube. You can use the method .confirmCardPayment taking any of the elements. Something like:
(token) => {
stripe?.confirmCardPayment(token, {
payment_method: {
card: elements?.getElement(CardNumberElement),
billing_details: {
name: "Leandro",
},
},
});
}
it will still validate all fields. Testing in the sandbox, I was passing like so but testing with leaving CVC empty, and stripe API was rejecting.

Related

How to create a comment and reply section with MERN STACK

I created a comment session on my entertainment website
It’s working on backend.
It’s working on the frontend also but it’s not displaying the content the user typed on the database
This is my frontend (Comment form) logic:
export default function AddComment({ busy}) {
const [content, setContent] = useState("");
const { movieId } = useParams();
const { updateNotification } = useNotification();
const handleOnChange = ({ target }) => {
setContent(target.value);
};
const handleSubmit = async (e) => {
e.preventDefault();
const { error, message } = await addComment(movieId);
if (error) return updateNotification("error", error);
updateNotification("success", message);
const newComment = {
content,
};
setContent(newComment);
setContent("");
};
return (
<div className='p-5'>
<br />
<p className='dark:text-white text-primary'>replies</p>
<hr className='w-64' />
{/* Comment Lists */}
{/* Root Comment Form */}
{/* Form */}
<form className='flex ' onSubmit={handleSubmit} busy={busy}>
<textarea
value={content}
onChange={handleOnChange}
type='text'
autoComplete='text'
className='w-full rounded-md p-2 dark:text-white text-primary outline-none bg-transparent resize-none border-b focus:border-blue-500'
placeholder='Add New comment'
/>
<br className='dark:text-white text-primary ' />
<button
type='submit'
className=' w-5 h-14 dark:text-white text-primary bg-blue-600 hover:bg-blue-400 focus:border-blue-900 rounded-md'
>
{busy ? <ImSpinner3 className='animate-spin' /> : "Add"}
</button>
</form>
</div>
);
}
Then the addComment is coming from this API:
import { catchError, getToken } from "../utils/helper";
import client from "./client";
export const addComment = async (movieId, newComment) => {
const token = getToken();
try {
const { data } = await client.post(
`/comments/comment/${movieId}`,
newComment,
{
headers: {
authorization: "Bearer " + token,
},
}
);
return data;
} catch (error) {
return catchError(error);
}
};
The backend is working:
exports.createComment = expressAsyncHandler(async (req, res) => {
const { movieId } = req.params;
const { content } = req.body;
const userId = req.user._id;
console.log(req.body);
// verify user before comment
if (!req.user.isVerified)
return sendError(res, "Please verify your email first!");
if (!isValidObjectId(movieId)) return sendError(res, "Invalid Movie!");
// create and update new comment
const newComment = new Comment({
user: userId,
parentMovie: movieId,
content,
});
// save new comment
await newComment.save();
res.json({ message: "New comment added!!", newComment });
});
I posted with Postman on backend it gave me this on the database:
_id
:
62dcfccd93444cef55611632
user
:
62bf20d65073a7c65f549078
parentMovie
:
62c2c425465804ff32cdd06c
content
:
"hello"
createdAt
:
2022-07-24T08:03:25.666+00:00
updatedAt
:
2022-07-24T08:03:25.666+00:00
__v
:
0
on the console:
The port is listening on port 8000
connected to db
{ content: 'hello' }
POST /api/comments/comment/62c2c425465804ff32cdd06c 200 447.534 ms - 260
I posted on the frontend it gave me this on the database, no content:
_id
:
62dcfd6993444cef55611635
user
:
62bf57e8a8f3e737b2af23d9
parentMovie
:
62cc1d426785cfe42f8737a8
createdAt
:
2022-07-24T08:06:01.458+00:00
updatedAt
:
2022-07-24T08:06:01.458+00:00
__v
:
0
on the console it shows an empty object:
{}
POST /api/comments/comment/62cc1d426785cfe42f8737a8 200 364.009 ms - 242
This is how I solved the problem
Hope this solution will help many
const handleSubmit = async (e) => {
e.preventDefault();
const { error, message } = await addComment(movieId, content); // call the content and movieId from backend
if (error) return updateNotification("error", error);
updateNotification("success", message);
// push and display the content on database
const newComment = {
content,
};
setContent(newComment);
setContent("");
};
Then the API should be like this
export const addComment = async (movieId, newComment) => {
const token = getToken();
// console.log(newComment);
const body = {
content: newComment,
};
try {
const { data } = await client.post(`/comments/comment/${movieId}`, body, {
headers: {
authorization: "Bearer " + token,
},
});
return data;
} catch (error) {
return catchError(error);
}
};

star rating value from React to Mongodb

I am working to show the rating for every college that is shown to user based on user score. Suppose user A gives college 1 as rating 5, then the rating will be send to mongodb database
In database I have made a collection named rating
ratingSchema = mongoose.Schema({
rating: Number,
userId: {
ref: 'users',
type: mongoose.SchemaTypes.ObjectId
// a string or whatever your id is
},
collegeId: {
ref: 'colleges',
type: mongoose.SchemaTypes.ObjectId
},
})
const Rating = mongoose.model('rating', ratingSchema);
The query written in nodeJS file is:
Rating
.findOne({})
.populate('userId')
.populate('collegeId');
How to post the rating from react to mongodb.
the function made in react is :
component/Report.js
const StarRating = (props) => {
console.log(props);
return (
<div>
{Array(5)
.fill(0)
.map((_, idx) => (
<label key={idx}>
<input
type="radio"
name="rating"
onChange={handleRate}
value={props.ratingValue}
checked={idx === props.ratingValue}
/>
<FaStar color={idx < 3 ? "#01af93" : "#bbb"} />
</label>
))}
</div>
);
};
const Report = (props) => {
const { advices } = useSelector((state) => state.advice);
const [rate, setRating] = useState(null);
useEffect(() => {
if (!advices) {
dispatch(fetchAdvices(history));
}
});
useEffect(() => {
async function fetchRate() {
try {
const { rating } = await api.get(paths.FETCH_RATING);
console.log(rating + "user rating");
} catch(error) {
console.log(error);
}
};
fetchRate();
}, []);
const handleRate = async() => {
const rate = await api.post(paths.UPDATE_RATING, {rating:rate});
props.setRating(rate)
}
return (
<>
<Container>
<Grid>
<Fragment>
<Grid >
<Card>
<CardContent><> <div>
<StarRating setRating={(val) => setRate(val)} ratingValue={rate} />
</div></></CardContent>
</Card>
</Grid>
</>
)};
i want to set rating for college and next time when user sees the order of colleges will be in the highest ranking to lowest ranking.
const router = express.Router();
router.post('/ratings', async (req, res) => {
const { rating, collegeId } = req.body;
// just an example to userId, depends on your authentication method
const userId = req.user.id
// example validation
if (!rating || !collegeId) {
return res.status(400).send({ message: 'bad request.' });
}
try {
const newRating = new Rating({rating, collegeId, userId})
await newRating.save()
return res.status(201).json({message:"success", rating:newRating})
} catch (error) {
res.status(500).send({ message: 'database error' });
}
});
then you can axios.post("yourApiUrl/ratings", {rating: 5, collegeId:123456})
router.get('/ratings', async (req, res) => {
try {
// maybe you need modify I didn't memorize mongoose methods
const ratings = await Rating.find({userId}
.populate('userId')
.populate('collegeId').sort({rating:1});
return res.status(201).json({message:"success", ratings})
} catch (error) {
res.status(500).send({ message: 'database error' });
}
});
this route gives ordered by user ratings.

Have a Handler Change Value of a Variable in Mongodb

I am working on a product where I have pre orders and normal orders. For my pre order items, I have the variable pre_orderQty, and for my normal items, I have the variable qty. In my OrderScreen.js, I have a handler that deals with if an order has been shipped (shipHandler). The issue that I am having is that I am trying to get this handler to also update any items in my order that have a qty value of 0 to the value of the pre_orderQty when clicked.
I am using node in my backend and react in my frontend. The orders are stored in mongodb. I'm not exactly sure how to do this, and I would really appreciate any help or advice on how to accomplish this.
So far, I have attempted to do this by:
In my backend orderRouter.js
orderRouter.put(
'/:id/updated',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const order = await Order.findById(req.params.id);
if (order && order.orderItems.qty === 0) {
order.orderItems.qty = order.orderItems.pre_orderQty;
order.orderItems.pre_orderQty = 0;
const updatedOrder = await order.save();
res.send({ message: 'Order Updated', order: updatedOrder });
} else {
res.status(404).send({ message: 'Order Not Found' });
}
}),
);
Frontend:
OrderActions.js
export const updateOrder = (orderId) => async (dispatch, getState) => {
dispatch({ type: ORDER_UPDATE_REQUEST, payload: orderId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = Axios.put(
`/api/orders/${orderId}/updated`,
{},
{
headers: { Authorization: `Bearer ${userInfo.token}` },
},
);
dispatch({ type: ORDER_UPDATE_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: ORDER_UPDATE_FAIL, payload: message });
}
};
OrderScreen
import Axios from 'axios';
import { PayPalButton } from 'react-paypal-button-v2';
import React, { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import {shipOrder, deliverOrder, detailsOrder, payOrder, updateOrder } from '../actions/orderActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {
ORDER_DELIVER_RESET,
ORDER_SHIPPED_RESET,
ORDER_RETURN_RESET,
ORDER_PAY_RESET,
} from '../constants/orderConstants';
export default function OrderScreen(props) {
const orderId = props.match.params.id;
const [sdkReady, setSdkReady] = useState(false);
const orderDetails = useSelector((state) => state.orderDetails);
const { order, loading, error } = orderDetails;
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const orderShip = useSelector((state) => state.orderShip);
const {
loading: loadingShip,
error: errorShip,
success: successShip,
} = orderShip;
const orderUpdate = useSelector((state) => state.orderUpdate);
const {
loading: loadingUpdate,
error: errorUpdate,
success: successUpdate,
} = orderUpdate;
const dispatch = useDispatch();
useEffect(() => {
if (
!order ||
successShip ||
successUpdate ||
(order && order._id !== orderId)
) {
dispatch({ type: ORDER_SHIPPED_RESET });
dispatch({ type: ORDER_UPDATE_RESET });
dispatch(detailsOrder(orderId));
} else {
if (!order.isPaid) {
if (!window.paypal) {
addPayPalScript();
} else {
setSdkReady(true);
}
}
}
}, [dispatch, order, orderId, sdkReady, successShip, successUpdate, order]);
const shipHandler = () => {
dispatch(shipOrder(order._id));
dispatch(updateOrder(order._id));
};
return loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>
<h1>Order {order._id}</h1>
<div className="row top">
<div className="col-1">
<div className="card card-body">
<ul>
{userInfo.isAdmin && (
<li>
{loadingShip && <LoadingBox></LoadingBox>}
{errorShip && (
<MessageBox variant="danger">{errorShip}</MessageBox>
)}
<button
type="button"
className="primary block"
onClick={shipHandler}
>
Order Shipped
</button>
</li>
)}
</ul>
</div>
</div>
</div>
</div>
);
}

Why axios is not posting data to server for stripe?

Im followin a youtube tutorial to do payments but I am stucked at a pace that axios could not post data to server
code
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import Abc from './Abc';
import './index.css'
ReactDOM.render(
<Abc/>
,
document.getElementById('root')
);
Abc.js
import React from "react";
import { loadStripe } from "#stripe/stripe-js";
import {
Elements,
CardElement,
useStripe,
useElements
} from "#stripe/react-stripe-js";
import axios from "axios";
const CheckoutForm = ({ success }) => {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async event => {
event.preventDefault();
const { error, paymentMethod } = await stripe.createPaymentMethod({
type: "card",
card: elements.getElement(CardElement)
});
if (!error) {
const { id } = paymentMethod;
try {
const { data } = await axios.post("/api/charge", { id, amount: 1099 });
console.log(data);
success();
} catch (error) {
console.log('error is => ',error);
}
}
};
return (
<form
onSubmit={handleSubmit}
style={{ maxWidth: "400px", margin: "0 auto" }}
>
<h2>Price: $10.99 USD</h2>
<img
src="https://images.ricardocuisine.com/services/recipes/500x675_7700.jpg"
style={{ maxWidth: "50px" }}
alt='abc'
/>
<CardElement />
<button type="submit" disabled={!stripe}>
Pay
</button>
</form>
);
};
// you should use env variables here to not commit this
// but it is a public key anyway, so not as sensitive
const stripePromise = loadStripe("pk_test_51JsQsfBbWBJ638dRkTi29yzu85fW6JAvGzbJo9f5RgOtOogcpKnzCfJo6VJoKGemEW54wxrDebWpM8V6vKJl36mC00K3JPAmHr");
const Abc = () => {
const [status, setStatus] = React.useState("ready");
if (status === "success") {
return <div>Congrats on your empanadas!</div>;
}
return (
<Elements stripe={stripePromise}>
<CheckoutForm
success={() => {
setStatus("success");
}}
/>
</Elements>
);
};
export default Abc;
charge.js
import Stripe from "stripe";
const stripe = new Stripe("sk_test_51JsQsfBbWBJ638dRR3Iryb907XNtHaeVYhtCRp6SDmaiWmQg51ys2wdB3z6HJ8svutnA8HPMp5yEtdxTSParn3uN00Xb3PJd4o");
export default async (req, res) => {
const { id, amount } = req.body;
try {
const payment = await stripe.paymentIntents.create({
amount,
currency: "USD",
description: "Delicious empanadas",
payment_method: id,
confirm: true
});
console.log(payment);
return res.status(200).json({
confirm: "abc123"
});
} catch (error) {
console.log(error);
return res.status(400).json({
message: error.message
});
}
};
but this is giving me error when submitting xhr.js:210 POST http://localhost:3000/api/charge 404 (Not Found)
Hierarchy
any help will be appreciated.I was following https://www.youtube.com/watch?v=WTUYem2IxLA&ab_channel=LeighHalliday tutorial
Assuming you have Next.js set up properly, your api folder needs to be in the /pages directory.
https://nextjs.org/docs/api-routes/introduction

how to create poll using API with react functional component

this is my react js code and I want to connect with my node js API but I don't understand how to that ...!
import React, { useState } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([...answers]);
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}
It work's fine with
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
but I want to connect this poll with my API instead of Declaring it ..! this is my api- to get data -> ( router.get("/poll/:pollId", getPoll); //)
exports.getPoll = async (req, res, next) => {
try {
const { pollId } = req.params;
const polls = await Poll.findById(pollId);
if (!polls) throw new Error("no polls found");
res.status(200).json(polls);
} catch (error) {
error.status = 400;
next(error);
}
};
This is a postman image -
and this API for POST data- and my node js code -
exports.votes = async (req, res, next) => {
try {
/**
* 1. get the poll from db
* 2. check if the user already exists in any option
* 3. if user has already selected any option do nothing
* 4. if user has selected any other option remove from that option
* 5. if user does not exist in any option, insert his user id to selected option
*/
const { pollId } = req.params;
let { userId, answer } = req.body;
// get selected poll from db
const poll = await Poll.findById(pollId);
if (answer && poll) {
answer = answer.toLowerCase();
///Finf the Poll
let existingVote = null;
Object.keys(poll.options).forEach((option) => {
// loop on all options, check if the user already exists in any option
if (poll.options[option].includes(userId)) {
existingVote = option;
}
});
if (existingVote == null) {
// if there is no existing vote save it to db
try {
const push = {};
push[`options.${answer}`] = userId;
const update = await Poll.findByIdAndUpdate(
pollId,
{ $push: push },
{ upsert: true }
);
res.status(201).json(update);
} catch (err) {
error.status = 400;
next(error);
}
} else if (existingVote && existingVote.length > 0) {
// check if answer is same as previous, if yes send not modified
if (existingVote.toLowerCase() === answer.toLowerCase()) {
res.status(304).send("Response already saved");
} else {
// delete the previous response and save it in new
if (
Array.isArray(poll.options[existingVote]) &&
poll.options[existingVote].length > 0
) {
// TODO: filtering this is not returning array but 1
poll.options[existingVote] = poll.options[existingVote].filter(
(vote) => vote != userId
);
poll.options[answer] = poll.options[answer].push(userId);
const update = await Poll.findByIdAndUpdate(pollId, {
$set: { options: poll.options },
});
res.status(201).json(update);
}
}
} else {
error = {
status: 500,
message: "Something went wrong",
};
next(error);
}
} else {
error = {
status: 404,
message: "Poll not found",
};
next(error);
}
} catch (error) {
error.status = 400;
next(error);
}
};
this is a POSTMAN image using POST to store data --- >
how can I connect API with react poll
What you'd do is make a fetch() to your /api/polls endpoint inside your Fakepolls component, the URL being exactly as you show in your Postman screenshot. More info on fetch here at the MDN docs.
With the response you get from the endpoint, populate the answers array you component uses. From what I see, it would require a bit of transformation as your answer object is not quite the same as what Poll needs.
Next, upon user action, as well as updating the votes in the UI, you need to make another fetch to your vote endpoint.
Here's your component again with these adjustments. Keep in mind it's untested and the URLs are obviously not real:
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollQuestion, setPollQuestion] = useState('');
const [pollAnswers, setPollAnswers] = useState([]);
// Query the actual poll info from the server
useEffect(() => {
fetch('http://your-server/api/polls/you-poll-id')
.then((response) => response.json()) //parse response as json
.then((pollObject) => {
let answerCountDictionary = Object.keys(pollObject.options)
.map(oKey => {
return {
option: oKey,
anwers: pollObject.options[oKey].length
}
}); //iterate over the 'options' properties' keys to get the names and the current votes
setPollAnswers(answerCountDictionary);
setPollQuestion(pollObject.question)
})
.catch((error) => {
console.error('Error:', error);
});
},[]) //adding empty array of dependencies to prevent multiple calls on state change
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
//also submit the backend
fetch('http://your-server/api/vote/poll-id', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
"userId": "the-logged-in-user",
"answer": voteAnswer
},
})
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}

Resources