Stripe - how do I save card element in react? - reactjs

I'm trying to save card details for use later.
I have generated the SetupIntent client secret
I'm trying to use confirm card setup.
I'm following the docs here for react.
The following line:
const cardElement = this.props.elements.getElement('card')
is throwing me this error:
TypeError: Cannot read property 'getElement' of undefined
Where am I going wrong? My code is below:
This is the relevant portion of the main component:
import React from "react";
import { Elements, StripeProvider } from "react-stripe-elements";
import SaveCardForm from "./SaveCardForm";
<StripeProvider
apiKey={process.env.REACT_APP_API_STRIPE_PUBLISH}
>
<Elements>
<SaveCardForm/>
</Elements>
</StripeProvider>
And this is the SaveCardForm component
import React, { Component } from "react";
import { Stripe, CardElement, injectStripe } from "react-stripe-elements";
import axios from "axios";
class SaveCardForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit = e => {
e.preventDefault()
const cardElement = this.props.elements.getElement('card');
axios.get(`${process.env.REACT_APP_API}/saveCardDetails`).then(res => {
console.log('res.data', res.data)
this.props.stripe.confirmCardSetup(res.data.client_secret, {
payment_method: {
card: cardElement,
},
}).then( confirmCardSetupRes => {
console.log('confirmCardSetupRes', confirmCardSetupRes)
})
})
}
render() {
return (
<div>
<CardElement />
<button onClick={this.submit}>
Bid For Tickets
</button>
</div>
);
}
}
export default injectStripe(SaveCardForm);

Given your components, there is no prop named elements passed into SaveCardForm. If it's access to CardElement you are after, use a ref which will give you a direct reference to that component e.g.
constructor(props) {
...
this.cardEl = React.createRef();
}
submit = e => {
...
const card = this.cardEl.current.<accessDomHere>;
this.props.stripe.confirmCardSetup(res.data.client_secret, {
payment_method: {
card
},
}).then(...)
}
render() {
...
<div>
<CardElement ref={this.cardEl} />
...
</div>
}
Switch out <accessDomHere> for whatever DOM query you need to perform to get the information you need. There may even be a React property or function you can access (I'm not familiar with the component).

I resolved this by updating to the latest version of react-stripe-elements.
There is an error in the versions before 5.1.0

Related

Having trouble rendering data in react component

I'm trying to render the following the 'dogName' value of the following array to the browser, but it's coming up as 'undefined':
[
{
"id": 1,
"dogName": "bruce"
},
{
"id": 2,
"dogName": "borker"
},
{
"id": 3,
"dogName": "henry"
}
]
So, first of all, the data is pulled from a database and set in state in the parent component, where's it's passed as props to the child component 'DogNameList' (which I've trimmed down to just the relevant bits):
import React from 'react';
import './styles.css'
import DogList from './DogList'
import Dogue from './Dogue'
import axios from 'axios'
import DogNameList from './DogNameList'
class App extends React.Component {
constructor(){
super()
this.state = {
**dogName:[]**
}
}
componentDidMount() {
axios.get('http://localhost:3000/dogs')
.then(res => {
this.setState({
**dogName:res.data**
})
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div>
<DogNameList **names = {this.state.dogName}**/>
<Dogue/>
</div>
);
}
}
export default App;
In DogNameList, the data is mapped over and then passed as props to the 'Dogue' component (stupid names, I know, but this is a personal project):
import React from 'react'
import Dogue from './Dogue'
const DogNameList = (props) => {
return(
<div>
{
props.names.map(name => {
console.log(name.dogName)
return <Dogue name = {name} key ={name.id}/>
})
}
</div>
)
}
export default DogNameList
finally, it's supposed to be rendered to the browser via the 'Dogue' component:
import React from 'react'
import axios from 'axios'
class Dogue extends React.Component {
constructor(props){
super(props)
this.state = {
}
}
render(){
return (
<div>
<img className = 'img' src = {this.props.dogList}/>
<br/>
<form className = 'form'>
<input type = 'text' placeholder = 'Enter dog name'/>
<br/>
<button>Submit</button>
</form>
**<h2>dog name: {this.props.name}</h2>**
</div>
)
}
}
export default Dogue
Any ideas why it's not working? I console logged the following and it returned the list of names (not as strings, I should add):
props.names.map(name => {
console.log(name.dogName)
First of all, replace this
<h2>dog name: {this.props.name}</h2>
with this
<h2>dog name: {this.props.name.dogName}</h2>
because you are creating a component with object, so name property actually holds the object, not the name property of the object.
return <Dogue name = {name} key ={name.id}/>
You also don't declare somewhere this property
{this.props.dogList}
Also to handle the undefined error messages, do this
{this.state.dogName && <DogNameList names ={this.state.dogName}/>}

Not sure if i'm using react context correcly

I've created a form in react and after some research i think that if you don't want to use an external library to manage the form, the context could be the best choice, expecially in my case where i've many nested component that compose it.
But, i'm not sure that putting a function inside my state is a good thing.
But let me give you some code:
configuration-context.js
import React from 'react'
export const ConfigurationContext = React.createContext();
ConfigurationPanel.jsx
import React, { Component } from 'react'
import { Header, Menu, Grid } from 'semantic-ui-react'
import ConfigurationSection from './ConfigurationSection.jsx'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationPanel extends Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
configuration: {
/* the configuration values */
banana: (data) => /* set the configuration values with the passed data */
}
}
}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
return (
<ConfigurationContext.Provider value={this.state.configuration}>
<Grid.Row centered style={{marginTop:'10vh'}}>
<Grid.Column width={15} >
<div className='configuration-panel'>
/* SOME BUGGED CODE */
<div className='configuration-section-group'>
{this.props.data.map((section, i) => <ConfigurationSection key={i} {...section} />)}
</div>
</div>
</Grid.Column>
</Grid.Row>
</ConfigurationContext.Provider>
)
}
}
ConfigurationItem.jsx
import React, { Component } from 'react'
import { Input, Dropdown, Radio } from 'semantic-ui-react'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationItem extends Component {
static contextType = ConfigurationContext
constructor(props) {
super(props)
}
handleChange = (e, data) => this.context.banana(data)
itemFromType = (item) =>{
switch (item.type) {
case "toggle":
return <div className='device-configuration-toggle-container'>
<label>{item.label}</label>
<Radio name={item.name} toggle className='device-configuration-toggle'onChange={this.handleChange} />
</div>
/* MORE BUGGED CODE BUT NOT INTERESTING*/
}
}
render() {
return this.itemFromType(this.props.item)
}
}
So, at the end i've a ConfigurationContext that is just a declaration, everything is inside the parent state.
The thing that i don't like is putting the banana function inside the state (it will have more logic that just logging it)
What do you think about it?
Any suggestion is appreciated.
Thanks
banana is just a regular function and you do not have to put it in the state, just do:
class ConfigurationPanel extends Component {
banana = data => console.log(data)
...
render() {
return (
<ConfigurationContext.Provider value={{banana}}>
...
}
After that you can use this.context.banana(data) as normal.

How to use map on multi objects array in React

This is child component as i can you Props here
Child Component:
import React from "react";
const PeopleList = props => {
console.log("child Props :", props.data);
const list = props.data.map(item => item.name);
return <React.Fragment>{"list"}</React.Fragment>;
};
export default PeopleList;
Main Component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPeople } from "../actions/peopleaction";
import PeopleName from "../containers/peopleName";
class Main extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(fetchPeople());
}
render() {
const { Error, peoples } = this.props;
console.log("data", peoples);
return (
<div className="main">
{"helo"}
<PeopleName data={peoples.results} />
</div>
);
}
}
const mapStateToProps = state => {
return {
peoples: state.peoples.peoples,
error: state.peoples.error
};
};
export default connect(mapStateToProps)(Main);
If i iterate the props multi objects array i can face Map is not define issue;
I need to iterate the props.data multi objects array in child component and i get object from Redux store. once component loaded the redux store.
can you please some one help me on this.
you can find whole code below mentioned
Try this It works in your codesandbox.
{peoples.results && <PeopleName data={peoples.results} />}

Render HOC(Component) without changing Component Name in JSX

I have two HOCs that add context to a component like so :
const withContextOne = Component => class extends React.Component {
render() {
return (
<ContextOne.Consumer>
{context => <Component {...this.props} one={context} /> }
</ContextOne.Consumer>
);
}
};
export default withContextOne;
Desired Result
I just want an syntactically concise way to wrap a component with this HOC so that it doesn't impact my JSX structure too much.
What I have tried
Exporting a component with the HOC attached export default withContextOne(withContextTwo(MyComponent)) This way is the most concise, but unfortunately it breaks my unit tests.
Trying to evaluate the HOC from within JSX like :
{ withContextOne(withContextTwo(<Component />)) }
This throws me an error saying
Functions are not valid as a React child. This may happen if you return a Component instead of < Component /> from render.
Creating a variable to store the HOC component in before rendering :
const HOC = withContextOne(Component)
Then simply rendering with <HOC {...props}/> etc. I don't like this method as it changes the name of the component within my JSX
You can set the displayName before returning the wrapped component.
const withContextOne = Component => {
class WithContextOneHOC extends React.Component {
render() {
return (
<ContextOne.Consumer>
{context => <Component {...this.props} one={context} /> }
</ContextOne.Consumer>
);
}
}
WithContextOneHOC.displayName = `WithContextOneHOC(${Component.displayName})`;
return WithContextOneHOC;
};
This will put <WithContextOneHOC(YourComponentHere)> in your React tree instead of just the generic React <Component> element.
You can use decorators to ease the syntactic pain of chained HOCs. I forget which specific babel plugin you need, it might (still) be babel-plugin-transform-decorators-legacy or could be babel-plugin-transform-decorators, depending on your version of babel.
For example:
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { resizeOnScroll } from './Resize';
#withRouter
#resizeOnScroll
#injectIntl
#connect(s => s, (dispatch) => ({ dispatch }))
export default class FooBar extends Component {
handleOnClick = () => {
this.props.dispatch({ type: 'LOGIN' }).then(() => {
this.props.history.push('/login');
});
}
render() {
return <button onClick={}>
{this.props.formatMessage({ id: 'some-translation' })}
</button>
}
}
However, the caveat with decorators is that testing becomes a pain. You can't use decorators with const, so if you want to export a "clean" undecorated class you're out of luck. This is what I usually do now, purely for the sake of testing:
import React, { Component } from 'react';
import { withRouter } from 'react-router';
import { injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { resizeOnScroll } from './Resize';
export class FooBarUndecorated extends Component {
handleOnClick = () => {
this.props.dispatch({ type: 'LOGIN' }).then(() => {
this.props.history.push('/login');
});
}
render() {
return <button onClick={}>
{this.props.formatMessage({ id: 'some-translation' })}
</button>
}
}
export default withRouter(
resizeOnScroll(
injectIntl(
connect(s => s, ({ dispatch }) => ({ dispatch }))(
FooBarUndecorated
)
)
)
);
// somewhere in my app
import FooBar from './FooBar';
// in a test so I don't have to use .dive().dive().dive().dive()
import { FooBarUndecorated } from 'src/components/FooBar';

React not passing props to children?

I'm trying to pass the data from this axios call into a child component, Hero. Despite having passed down the props and made a successful axios call it won't actually make it into the Hero div.
When I console.log on the child component it claims to have the data but then fails to push it to the champions array so I can't use it. Any ideas?
Edit:
I'll add in here that I do have react-router installed in this project however this data is being passed around across one "view" and not multiple pages.
This is the parent component
import React, { Component } from 'react';
import axios from 'axios';
import './assets/stylesheets/screen.css';
import Hero from './Hero';
import Info from './Info';
class Home extends Component {
constructor(props){
super(props);
this.state = { champions: [] };
}
componentDidMount() {
axios.get(
'https://api.pinterest.com/v1/boards/gasulliv/pose-
references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-
m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2
Curl%2Coriginal_link%2Cimage').then(champions => {
this.setState({ champions });
console.log(champions);
});
}
render() {
return (
<div>
<Hero champions = {this.state.champions} />
<Info />
</div>
);
}
}
export default Home;
And this is child component (at this console log I get two answers, one claiming it has the data and another claiming it does not):
import React from 'react';
import Header from './Header';
import 'bootstrap/dist/css/bootstrap.min.css';
import './assets/stylesheets/screen.css';
const Hero = (props) => {
console.log(props);
return (
<div className = "jumbotron kindred">
<Header />
<div className = "textHolder">{ props.champions.length }</div>
</div>
)
}
export default Hero;
You have to access the data in the data key response.data
Try the following.
axios.get('https://api.pinterest.com/v1/boards/gasulliv/pose-references/pins/?access_token=AQjW6hDdAF0egwEesZA6oJbqP0XQFQ-m6_jg2RpErKPqdSA7cQAAAAA&limit=100&fields=id%2Clink%2Cnote%2Curl%2Coriginal_link%2Cimage')
.then((response) => {
this.setState({
champions: response.data
})
})
.catch((error) => {
// Do something with the error
})
Thanks for help but it turns out the issue had to do with the fact that I had the router installed. Likely I just need to pass that data around through the router instead of the pages.
Kudos for the help!

Resources