How display payment succeeded without return_url param in Stripe - stripe-payments

I have one problem in integration Stripe into my React application. I use code from official Stripe documentation. It works expected. My question is how to check is payment succeeded without using return_url ? Am I required to use return url ? I found in Stripe documentation redirect: "if_required" option, but that doesnt make anything. I just get error problem in my console if I put this object in confirmPayment method. I would like have scenario is payment successfull that client navigate to some Confirmation page and to get message payment successfully.
App.jsx
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import CheckoutForm from "./CheckoutForm";
import "./App.css";
// Make sure to call loadStripe outside of a component’s render to avoid
// recreating the Stripe object on every render.
// This is your test publishable API key.
const stripePromise = loadStripe("pk_test_51LmE9VAoYs2flpvClDqeh0f1vhaDUkBM0bRGaJgThjtaMd3PiPUGQOHjn9f7XW1HGgSQBvTq3xoLy9PovlWLPUnR0031srjgyb");
export default function App() {
const [clientSecret, setClientSecret] = useState("");
useEffect(() => {
// Create PaymentIntent as soon as the page loads
fetch("/create-payment-intent", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ items: [{ id: "xl-tshirt" }] }),
})
.then((res) => res.json())
.then((data) => setClientSecret(data.clientSecret));
}, []);
const appearance = {
theme: 'stripe',
};
const options = {
clientSecret,
appearance,
};
return (
<div className="App">
{clientSecret && (
<Elements options={options} stripe={stripePromise}>
<CheckoutForm />
</Elements>
)}
</div>
);
}
CheckoutForm.jsx
import {
PaymentElement,
useStripe,
useElements
} from "#stripe/react-stripe-js";
export default function CheckoutForm() {
const stripe = useStripe();
const elements = useElements();
const [message, setMessage] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
if (!stripe) {
return;
}
const clientSecret = new URLSearchParams(window.location.search).get(
"payment_intent_client_secret"
);
if (!clientSecret) {
return;
}
stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent }) => {
switch (paymentIntent.status) {
case "succeeded":
setMessage("Payment succeeded!");
break;
case "processing":
setMessage("Your payment is processing.");
break;
case "requires_payment_method":
setMessage("Your payment was not successful, please try again.");
break;
default:
setMessage("Something went wrong.");
break;
}
});
}, [stripe]);
const handleSubmit = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
// Stripe.js has not yet loaded.
// Make sure to disable form submission until Stripe.js has loaded.
return;
}
setIsLoading(true);
const { error } = await stripe.confirmPayment({
elements,
confirmParams: {
// Make sure to change this to your payment completion page
return_url: "http://localhost:3000",
},
});
// This point will only be reached if there is an immediate error when
// confirming the payment. Otherwise, your customer will be redirected to
// your `return_url`. For some payment methods like iDEAL, your customer will
// be redirected to an intermediate site first to authorize the payment, then
// redirected to the `return_url`.
if (error.type === "card_error" || error.type === "validation_error") {
setMessage(error.message);
} else {
setMessage("An unexpected error occurred.");
}
setIsLoading(false);
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<PaymentElement id="payment-element" />
<button disabled={isLoading || !stripe || !elements} id="submit">
<span id="button-text">
{isLoading ? <div className="spinner" id="spinner"></div> : "Pay now"}
</span>
</button>
{/* Show any error or success messages */}
{message && <div id="payment-message">{message}</div>}
</form>
);
}

When using redirect: 'if_required', then the return_url attribute becomes not required.
If no redirection is required then you need to wait for the confirmation from the method stripe.confirmPayment and check if there is an error in the response.
To do so, you can adapt your CheckoutForm.jsx file and adapt your function handleSubmit like below:
setIsLoading(true);
const response = await stripe.confirmPayment({
elements,
confirmParams: {
},
redirect: 'if_required'
});
if (response.error) {
showMessage(response.error.message);
} else {
showMessage(`Payment Succeeded: ${response.paymentIntent.id}`);
}
setIsLoading(false);
Also, if you want to get notified from your backend when a successful payment has occurred, you can set a webhook[1] and listen to this Stripe event payment_intent.succeeded[2]
[1] https://stripe.com/docs/webhooks
[2] https://stripe.com/docs/api/events/types#event_types-payment_intent.succeeded

Related

NextJS component

I need to reload a remote JSON every 30 seconds. I currently do it this way in reactJS but since moving to NextJS it does not work
The issue is that the following work fine in my current ReactJS website but as soon as I Moved it to NextJS it printing our errors everywhere.
Mainly with the following
fetchTimeout
sessionStorage
export default function MediaControlCard(props) {
const fetchTimeout = (url, ms, { signal, ...options } = {}) => {
const controller = new AbortController();
const promise = fetch(url, { signal: controller.signal, ...options });
if (signal) signal.addEventListener("abort", () => controller.abort());
const timeout = setTimeout(() => controller.abort(), ms);
return promise.finally(() => clearTimeout(timeout));
};
const controller = new AbortController();
const podcast = props.podcast;
const classes = useStyles();
var token = uuidv4();
// alert(sessionStorage['uuid']);
if(!sessionStorage['uuid']){
sessionStorage.setItem("uuid",token);
}
if(!sessionStorage['station']){
sessionStorage.setItem("station","DRN1");
}
if(!sessionStorage['live']){
sessionStorage.setItem("live",true);
}
var icyStream = "https://api.drn1.com.au:9000/station/"+sessionStorage.station+"?uuid="+sessionStorage['uuid'];
var streamurl = icyStream;//window.com_adswizz_synchro_decorateUrl(icyStream);
React.useEffect(() => {
nowplaying();
document.getElementById("player").muted = false;
});
if(podcast){
alert('test');
}
/*if(!sessionStorage.getItem("station")){
sessionStorage.setItem("station","DRN1");
}*/
function nowplaying(){
// alert("hello");
if(sessionStorage.live === true){
document.getElementById("podcast-only").style.display='none';
}
fetchTimeout(`https://api.drn1.com.au:9000/nowplaying/`+sessionStorage.station+`?uuid=`+sessionStorage['uuid'], 3000, { signal: controller.signal })
.then(res => res.json())
.then(
(result) => {
//console.log("testing player"+result.data);
if(sessionStorage.getItem("live") === 'true'){
switch(result.data[0].track.songtype)
{
case "A":
AdSystem(result.data[0]);
break;
case "S":
Song(result.data[0]);
document.getElementById("Now_Playing_Artist").innerHTML = result.data[0].track.artist;
document.getElementById("Now_Playing_Title").innerHTML = result.data[0].track.title;
document.getElementById("Now_Playing_Cover").style.backgroundImage = "url('"+result.data[0].track.imageurl+"')";
break;
default:
Song(result.data[0]);
document.getElementById("Now_Playing_Artist").innerHTML = result.data[0].track.artist;
document.getElementById("Now_Playing_Title").innerHTML = result.data[0].track.title;
document.getElementById("Now_Playing_Cover").style.backgroundImage = "url('"+result.data[0].track.imageurl+"')";
break;
}
fetch(`https://itunes.apple.com/search?term=${result.data[0].track[0].artist}+${result.data[0].track[0].title}&limit=1`)
.then(res => res.json())
.then(
(result) => {
if(result.results[0]){
document.getElementById("buylink").href = result.results[0].collectionViewUrl;
document.getElementById("buynow").style.display = "block";
}
else
{
document.getElementById("buynow").style.display = "none";
}
})
}
})
.then(console.log)
.catch(error => {
console.error(error);
if (error.name === "AbortError") {
// fetch aborted either due to timeout or due to user clicking the cancel button
} else {
// network error or json parsing error
}
});
setTimeout(function(){nowplaying()}, 10000);
}
return (<>
<Card id="nowplayinginfo_card" className={classes.card}>
<CardMedia
id="Now_Playing_Cover"
className={classes.cover}
image="//tvos.adstichr.com/client/resources/images/stations/Indie/DRN1-Logo.png"
title="Live from space album cover"
/>
<div className={classes.details} id="adstichrNP">
<CardContent className={classes.content} id="song">
<Typography variant="subtitle1">
Now Playing
</Typography>
<Typography id="Now_Playing_Title" component="h6" variant="h6">
{props.artist}
</Typography>
<Typography id="Now_Playing_Artist" variant="subtitle1" color="textSecondary">
{props.song}
</Typography>
</CardContent>
<div id="buynow" className={classes.buynow}>
<a id="buylink" target="_blank" href="#Blank"><img alt="buynow" src="https://linkmaker.itunes.apple.com/assets/shared/badges/en-us/music-lrg-1c05919c6feae5d4731d4399cd656cd72e1fadc4b86d4bd7dc93cb8f3227cb40.svg"/></a>
</div>
<div id="podcast-only" className={classes.controls}>
<audio id="player" className={classes.player} controls controlsList="nodownload" autoPlay muted>
<source src={streamurl}
type="audio/mpeg"
/>
</audio>
</div>
</div>
</Card>
<Card className={classes.card} id="adbanner">
<CardContent className={classes.content} id="adstichr">
</CardContent>
</Card>
</>
)
}
How do I ac achieve this with NextJS. I thought anything I put into component with nextjs would just work the same as ReactJS - clearly not.
NextJS has server-side rendering features for your concern. I believe that you should use getStaticProps there is a special property in it called revalidate it will allow you to make requests on every timeout you wish to use. I took an example from official documentation of latest nextjs(version 11.0)
Docs: https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
export default Blog
You are not showing the errors but I suspect it is related to the server-side rendering feature of next.js.
document is defined only on the browser and since useEffect gets executed only on the browser you are calling nowPlaying inside the useEffect. That is the right thing. However sessionStorage (whatever is the package is) also has to be called on the browser.
You should be always retrieving the data from the storage inside useEffect, before component renders.
Yes, you can't achieve this with proper NextJS. I am using useSWR library, it has some "update" intervals as an option.
You can check it here. ("options" part)
you can use getServerSideProps. Make sure its a page component. getServerSideProps, getStaticProps only works in page component.
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page

ReactJS Stripe Payments not returning successful message after clicking pay with node and express

I am currently using ReactJS, node, and express with the Stripe Payment API. After clicking the pay button and entering the dummy credit card credentials, the page doesnt process the payment. I have entered the correct publishing key and api key that I got from my dashboard.I believe it likely has somnething to do with what I need to add in the server.js file(aka node backend).I have read through the docs for any clues I can get. Also have searched here on Stack Overflow. None of the questions had the same thing I was looking for. Please see below for pictures and code. Thanks
This is before pressing the button. Please Note the console on the right side.
This is after pressing the button. The loading spinner just displays forever. Also note the console on right side
// Donate.js
import React from "react";
import "./Donate.css";
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import CheckoutForm from "./CheckoutForm";
// Make sure to call `loadStripe` outside of a component’s render to avoid
// recreating the `Stripe` object on every render.
const stripe = loadStripe(
"pk*****************************"
);
stripe.then((data) => {
console.log(data);
});
const Donate = () => {
return (
<div className="donate">
<h1 className="donate__sectionHeader">Donate Now</h1>
<Elements stripe={stripe}>
<CheckoutForm />
</Elements>
</div>
);
};
export default Donate;
//CheckoutForm
import React, { useState, useEffect } from "react";
import { CardElement, useStripe, useElements } from "#stripe/react-stripe-js";
import "./CheckoutForm.css";
export default function CheckoutForm() {
const [succeeded, setSucceeded] = useState(false);
const [error, setError] = useState(null);
const [processing, setProcessing] = useState("");
const [disabled, setDisabled] = useState(true);
const [clientSecret, setClientSecret] = useState("");
const stripe = useStripe();
const elements = useElements();
useEffect(() => {
// Create PaymentIntent as soon as the page loads
window
.fetch("/donate", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ items: [{ id: "xl-tshirt" }] }),
})
.then((res) => {
return res.json();
})
.then((data) => {
setClientSecret(data.clientSecret);
});
}, []);
const cardStyle = {
style: {
base: {
color: "#32325d",
fontFamily: "Arial, sans-serif",
fontSmoothing: "antialiased",
fontSize: "16px",
"::placeholder": {
color: "#32325d",
},
},
invalid: {
color: "#fa755a",
iconColor: "#fa755a",
},
},
};
const handleChange = async (event) => {
// Listen for changes in the CardElement
// and display any errors as the customer types their card details
setDisabled(event.empty);
setError(event.error ? event.error.message : "");
};
const handleSubmit = async (ev) => {
ev.preventDefault();
setProcessing(true);
const payload = await stripe.confirmCardPayment(clientSecret, {
payment_method: {
card: elements.getElement(CardElement),
},
});
if (payload.error) {
setError(`Payment failed ${payload.error.message}`);
setProcessing(false);
} else {
setError(null);
setProcessing(false);
setSucceeded(true);
}
console.log(clientSecret);
};
return (
<form id="payment-form" onSubmit={handleSubmit}>
<CardElement
id="card-element"
options={{ hidePostalCode: true, cardStyle }}
onChange={handleChange}
/>
<button disabled={processing || disabled || succeeded} id="submit">
<span id="button-text">
{processing ? <div className="spinner" id="spinner"></div> : "Pay"}
</span>
</button>
{/* Show any error that happens when processing the payment */}
{error && (
<div className="card-error" role="alert">
{error}
</div>
)}
{/* Show a success message upon completion */}
<p className={succeeded ? "result-message" : "result-message hidden"}>
Payment succeeded, see the result in your
<a href={`https://dashboard.stripe.com/test/payments`}>
{" "}
Stripe dashboard.
</a>{" "}
Refresh the page to pay again.
</p>
</form>
);
}
//server.js
const express = require("express");
const app = express();
const { resolve } = require("path");
// This is your real test secret API key.
const stripe = require("stripe")(
"sk_test_**********************************"
);
app.use(express.static("."));
app.use(express.json());
const calculateOrderAmount = (items) => {
// Replace this constant with a calculation of the order's amount
// Calculate the order total on the server to prevent
// people from directly manipulating the amount on the client
return 1400;
};
app.post("/create-payment-intent", async (req, res) => {
const { items } = req.body;
// Create a PaymentIntent with the order amount and currency
const paymentIntent = await stripe.paymentIntents.create({
amount: 1099,
currency: "usd",
// Verify your integration in this guide by including this parameter
metadata: { integration_check: "accept_a_payment" },
});
res.send({
clientSecret: paymentIntent.client_secret,
});
});
app.listen(4242, () => console.log("Node server listening on port 4242!"));
You need to review the server call/network response with the client_secret. The console error indicates you've provided an invalid secret to confirmCardPayment, apparently an empty string.
You specified: .
It would appear that your app is not setting the state via setClientSecret as intended, and you end up with the initial empty string value from useState("");.
Check your client_secret value before the confirmCardPayment call, and step backwards to find where the value is being dropped.

Stripe API - stipe.setupIntents doesn't exist

I am trying to delete setup intent via
stripe.setupIntents.delete
but that method doesn't exist. Any idea what am I doing wrong (I am looking at the official documents, stripe.setupIntents is what I need here).
I have the following code:
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe('{{ env('STRIPE_KEY') }}');
const elements = stripe.elements();
const cardElement = elements.create('card', {
hidePostalCode: true,
});
cardElement.mount('#card-element');
const cardHolderName = document.getElementById('card-holder-name');
const cardButton = document.getElementById('card-button');
const clientSecret = cardButton.dataset.secret;
cardButton.addEventListener('click', async (e) => {
e.preventDefault()
if (cardHolderName.value.length < 3) {
$(cardHolderName).addClass('is-invalid')
.parent()
.append('<span class="text-danger">Please Insert Card Holder Name.</span>');
return;
} else {
$(cardHolderName).removeClass('is-invalid');
$(cardHolderName).next('.text-danger').remove();
}
let id = $(cardButton).data('id')
let url = $(cardButton).data('url')
const { setupIntent, error } = await stripe.confirmCardSetup(
clientSecret, {
payment_method: {
card: cardElement,
billing_details: {
name: cardHolderName.value
}
}
}
)
if (error) {
let errorElement = document.getElementById('card-errors');
errorElement.textContent = error.message;
} else {
let billingForm = $('#billing-form')
$.ajax({
url: url,
method: 'POST',
cache: false,
data: {
cardholder: billingForm.find('#cardholder').val(),
locum_organization: billingForm.find('#locum_organization').val(),
billing_addresses: billingForm.find('#billing_addresses').val(),
intent: setupIntent,
id: id
},
success: function(response) {
},
fail: function(xhr, textStatus, errorThrown){
console.log(errorThrown);
},
error(response){
stripe.setupIntents.cancel(
setupIntent.id,
function(err, setupIntent) {
// asynchronously called
})
}
});
}
});
</script>
You can only cancel a setup intent; not delete it. Also, setup intents can only be cancelled server-side. Right now it looks like you're trying to cancel it client-side with Stripe.js (which doesn't have a cancel setup intent method). It also isn't necessary to cancel a setup intent after it's gone through. In fact, you can't cancel a setup intent at all unless it's in one of these four processing states:
requires_payment_method, requires_capture, requires_confirmation, requires_action.
https://stripe.com/docs/api/setup_intents/cancel?lang=node
Your integration seems fine other than that. I would recommend leaving out the cancellation step entirely, and you should be good to go!

Paypal Integration with reactjs -node.js -Guidance on implementation

Usage of smart payment buttons by PayPal:
how to use the smart payment button in Paypal ? when I am rendering the button I am getting errors , this is the script which I want to use here :
https://developer.paypal.com/demo/checkout/#/pattern/client
I am using the useEffect to load the script :
useEffect(() => {
const script = document.createElement('script');
script.src = "https://www.paypal.com/sdk/js?client-id=AZOEGr_kwtZWZ2_Hy46cHBs1pYR_Ly68zMWymBQX88AlUM70HifIuAbwaUW_92BXhCxAozxqFDBLU5wj&currency=USD";
script.async = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
}
}, []);
after that , I am rendering the summary of the registration in card where I want to render this Paypal smart button
<Card className={classes.card}>
<CardContent style={{textAlign:"center"}}>
.....
//registration details
.....
</CardContent>
<CardActions style={{justifyContent:"center"}} >
{renderPaypal}
// renderPaypal invokes the function to render the button
</CardActions>
</Card>
Here this is the function where I render the button :
const renderPaypal=()=>{
return (
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return fetch('/demo/checkout/api/paypal/order/create/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(data) {
return data.orderID;
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return fetch('/demo/checkout/api/paypal/order/' + data.orderID + '/capture/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
}
}).render('#paypal-button-container');
</script>
)
}
There is a guide here: https://developer.paypal.com/docs/checkout/reference/server-integration/#
But if you want to use the code in the samples directory, there is this one for the capture intent: https://github.com/paypal/Checkout-NodeJS-SDK/tree/develop/samples/CaptureIntentExamples
For your front-end web code, this is best: https://developer.paypal.com/demo/checkout/#/pattern/server
For refunding the payment, in the capture intent examples there is an example invocation of the refund method using a captureId : https://github.com/paypal/Checkout-NodeJS-SDK/blob/2476cd9c0ed7fdda5402bc7a2094522e6d719f5d/samples/CaptureIntentExamples/runAll.js
So that looks to be enough to figure it out how to use it

Cannot redirect to new page on first submission of form with history.push()

Edit
I've done some more debugging and here is the problem:
CreateProfile.js calls profileActions.createProfile() and passes data to be operated on and this.props.history so that it can push a new path onto the history stack.
profileActions.createProfile() successfully sends data to database. Database successfully uses the data.
profileActions.createProfile() pushes new path onto stack. The component at the path loads and successfully calls a reducer.
The URL in the browser does not reflect the path that is pushed onto the history stack. The new component does not load.
This only happens when creating an entry in the database. When updating an entry, the program works as expected.
I'm currently trying to redirect to a new page with react/redux. On the first submission, the form submits to the backend and creates an entry in the database but fails to redirect to the next page. On the second submission, however, it redirects just fine.
I'm using this.props.history.push() to do the redirect.
I think It may be an issue with the the response received from the backend but I cannot seem to figure out what the issue is. The reason I believe this is because it is hitting different logic because on the second submission, it is updating and not creating an entry.
Here is my component (CreateProfile.js)
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { createProfile } from '../../actions/profileActions';
import TextAreaGroup from '../common/TextAreaGroup';
import InputGroup from '../common/InputGroup';
class CreateProfile extends Component {
// Constructor
// componentWillRecieveProps()
onSubmit = (evt) => {
evt.preventDefault();
const profileData = {
handle: this.state.handle,
bio: this.state.bio,
website: this.state.website,
twitter: this.state.twitter,
instagram: this.state.instagram,
youtube: this.state.youtube,
linkedin: this.state.linkedin,
github: this.state.github,
vsco: this.state.vsco
};
this.props.createProfile(profileData, this.props.history);
}
//onChange()
render() {
// render logic
return (
// markup
<form onSubmit={this.onSubmit}>
// markup
<input
type="submit"
value="Create Profile"
className="btn btn-info btn-block mt-4"
/>
</form>
</div>
</div>
</div>
</div>
)
}
}
CreateProfile.propTypes = {
createProfile: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile,
errors: state.errors
});
export default connect(mapStateToProps, { createProfile })(withRouter(CreateProfile));
Here is my action file that submits to the backend (profileActions.js):
import axios from 'axios';
// import types
import { GET_PROFILE, PROFILE_LOADING, GET_ERRORS, CLEAR_CURRENT_PROFILE } from './types';
// Create Profile
export const createProfile = (profileData, history) => dispatch => {
axios.post('/api/profile', profileData)
.then(res => history.push('/login'))
.catch(err => {
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
})
};
}
And here is the route in my backend that is being submitted to:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validateProfileInputs(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
const profileFields = {}; //code setting fields omitted
Profile.findOne({user: req.user.id}).then(profile => {
if (profile) {
// Update Profile
Profile.findOneAndUpdate(
{ user: req.user.id },
{ $set: profileFields },
{ new: true }
).then(profile => res.json(profile)); // SUCCESSFUL PUSH ONTO THIS.PROPS.HISTORY
} else {
// Create Profile
// Check if handle exists
Profile.findOne({ handle: profileFields.handle })
.then(profile => {
if (profile) {
errors.handle = 'That handle already exists';
res.status(400).json(errors);
}
new Profile(profileFields).save().then(profile => res.json(profile)); // PUSH ONTO THIS.PROPS.HISTORY NOT OCCURRING
});
}
});
});
Any and all help would be greatly appreciated. I have tried my hardest but cannot seem to figure out what the issue is.
This problem arose because of my lack of understanding of how asynchronous javascript works.
The issue was with a few lines of code in the component that I was trying to push too.
componentDidMount() {
this.props.getProfile(); // Async function, sets profile object in store
}
render() {
const { profile } = this.state.profile;
if(!Object.keys(profile).length > 0) { // This is always evaluates to true
// because it executes before
// this.props.getProfile() returns
this.props.history.push('/create-profile');
}
}

Resources