I am trying to fetch API with react, however when I console it, it shows
parsing failed
function Error() { [native code] }
<constructor>: "Function"
name: "Function".
and I created a buttons inside the panel, when i click each button, the screen should show different information base on the data that i fetch from Api, however, when i trying to fetch API the buttons are not display in the screen anymore and the avatar image that I want to show on top of the information is also not display in the panel anymore, I have no idea where wrong. Also, the API url that I used only can random 500 results for each day. But I don't think this is the problem, since I try to use another link, it still a same problem. Thanks for your help!
index.js
const url = 'https://beta.randomapi.com/api/9qvib112?key=X7E9-7CWN-4TY0-7GZT&results=12';
class App extends Component {
constructor(props) {
super(props);
this.state = {
contacts: []
}
}
componentDidMount() {
this.fetchdata();
}
fetchdata() {
fetch(url)
.then(Response => Response.json())
.then(parsedJSON => console.log(parsedJSON.results.map(users => (
{
name: `${users.user.first} ${users.user.last}`,
birthday: `${users.birthday}`,
email: `${users.email}`,
address: `${users.address}`,
phone: `${users.phone}`,
password: `${users.password}`,
image: `${users.image}`,
}
))))
.then(contacts => this.setState({
contacts,
}))
.catch(erro => console.log('parsing failed', Error))
}
render() {
const {contacts} = this.state;
return (
<div className="panel">
{
contacts.length > 0 ? contacts.map(contact => {
return
<div class="panel">
<Panel
avatar={contact.image}
/>
<li class="flex-container">
<Button title="user">
<Panel user={contact.name} />
</Button>
<Button title="email">
<Panel user={contact.email} />
</Button>
<Button title="birthday">
<Panel user={contact.birthday} />
</Button>
<Button title="address">
<Panel user={contact.address} />
</Button>
<Button title="phone">
<Panel user={contact.phone} />
</Button>
<Button title="password">
<Panel user={contact.password} />
</Button>
</li>
</div>
}) : null
}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
ProfilePanel.js
const style={
borderRadius: 150,
}
class Panel extends Component {
constructor(props){
super(props);
this.state = {
avatar: "",
user: ""
}
}
render() {
const { avatar, user } = this.state;
return (
<div className="Panel">
<div class="panels">
<div className="avatar">
<img src={avatar} style={style}/>
</div>
</div>
<div class="center">
<h2 className="user">{user}</h2>
</div>
</div>
);
}
}
export default Panel;
Button.js
import './index.css';
import React, { Component } from 'react';
const styles = {
color: 'white',
background: '#0288d1',
margin: '20px',
width: 150,
height: 40,
borderRadius: 50,
marginTop: 0,
marginBottom: 40,
}
class Button extends Component {
constructor(props) {
super(props);
this.state = {
open:false,
};
}
render() {
const { title, children} = this.props;
const {open} = this.state;
return (
<div className={` ${open ? 'open' : ''}`}
class='button' onClick={(e) => this.handleClick(e)}>
<div className="panel-heading">
<h2 class='buttoncenter'>{title}</h2>
</div>
<div className="panel-collapse">
<div className="panel-body">
{children}
</div>
</div>
</div>
);
}
handleClick(e) {
e.preventDefault();
this.setState({
open: !this.state.open
})
}
}
export default Button;
In your fetch method, you're using an interface .then(Response => Response.json())
Try renaming it to something else like .then(res => res.json())
In index.js you're passing props to Panel and Button, but only Button is using them.
ProfilePanel.js const { avatar, user } = this.state; Change it to this.props
You could also just pass the "parsedJSON" in the .then directly to the state and then map through it like you're doing in the render method.
In your index.js file
Inside your fetchData() function
fetchdata() {
fetch(url)
.then(Response => Response.json())
.then(parsedJSON => console.log(parsedJSON.results.map(users => (
{
// Your Code
}
))))
.then(contacts => this.setState({contacts}))
.catch(error => console.log('parsing failed', Error()))
}
The Error in the catch statement is a function. You are using it as a property. Hence to get the the output of that function, you need to add parenthesis() after it.
Related
after onclick event occurs in backpackList.js, fetch data in context.js and then through setState I want to update noneUserCart . After that i want to get data from context.js to backpackList.js to show web page. but the data is inital data []. How can I solve this problem?!
I think this is a Asynchronous problem, but I'm new react, so I don't know how to write code for this. or do I use async, await.
Help me please!
import React, { Component } from 'react';
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;
class ProductProvider extends Component {
constructor() {
super();
this.state = {
totalProducts: 0,
isLogin: false,
cartList: [],
isNavOpen: false,
isCartOpen: false,
noneUserCart: [],
};
}
noneUserAddCart = bagId => {
fetch('/data/getdata.json', {
method: 'GET',
})
.then(res => res.json())
.catch(err => console.log(err))
.then(data => {
this.setState(
{
noneUserCart: [...this.state.noneUserCart, data],
},
() => console.log(this.state.noneUserCart)
);
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleCart: this.handleCart,
getToken: this.getToken,
addNoneUserCart: this.addNoneUserCart,
hanldeCheckout: this.hanldeCheckout,
openNav: this.openNav,
showCart: this.showCart,
habdleCartLsit: this.habdleCartLsit,
deleteCart: this.deleteCart,
noneUserAddCart: this.noneUserAddCart,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
export { ProductProvider, ProductConsumer };
import React, { Component } from 'react';
import { ProductConsumer } from '../../context';
export default class BackpackList extends Component {
render() {
const {
backpackdata,
backdescdata,
isdescOpen,
showDesc,
descClose,
rangenumone,
rangenumtwo,
} = this.props;
return (
<div>
{backdescdata.map((bag, inx) => {
return (
<>
{isdescOpen && bag.id > rangenumone && bag.id < rangenumtwo && (
<div className="listDescContainer" key={inx}>
<div className="listDescBox">
<ProductConsumer>
{value => (
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
</div>
)}
</ProductConsumer>
<span className="descClosebtn" onClick={descClose}>
X
</span>
</div>
</div>
</div>
)}
</>
);
})}
</div>
);
}
}
fetch is asynchronous, this.setState is yet called when console.log
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
{value.noneUserCart}
{/* when finished, result should show here */}
</div>
I want to use onClick on a stateless compoenent but it's reject an error like : onClick listener to be a function, instead got a value of object type.
I need to show and hide component on click.
Example when I click on the <ResultCard/> component I want to hide him and show <ResultDetail/>
State React Component :
import React, { Component } from "react";
import ResultCard from "./ResultCard";
import "../../assets/css/Result.css";
import Spinner from "../Spinner";
import { getApiToken, getParisByPrice } from "../../services/api";
import Modal from "../Modal";
import "../../assets/css/BudgetEntry.css";
import modify from "../../assets/images/modify.png";
import ResultDetail from "./ResultDetail";
class Results extends Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
closeResultDetails = () => {
this.setState({ showResult: true });
};
render() {
return (
<div className="results-container">
{this.state.loading ? (
<Spinner />
) : (
<div className={"row"}>
{this.state.showResult ?
(
this.state.paris.map(details => (
<ResultCard
key={details.id}
id={details.id}
showResultDetail={this.showResultDetail}
prefix={details.prefix}
costPerDay={details.average_cost_per_day}
logoSports={details.infrastructure.map(home =>
home.logo_path.map(path_image => (
<img
src={path_image}
alt="icon-sports"
style={{ width: 20 }}
key={path_image}
/>
))
)}
/>
))
)
:
(
<ResultDetail closeResultDetail={this.closeResultDetails}/>
)
}
</div>
)}
</div>
);
}
}
export default Results;
ResultCard (who is stateless component):
import React from 'react';
import '../../assets/css/ResultCard.css';
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id}) => {
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
)
};
export default ResultCard;
ResultDetail (who is stateless component):
import React from 'react';
const ResultDetail = (closeResultDetail) => (
<div onClick={closeResultDetail}>
<p>Result detail</p>
</div>
)
export default ResultDetail;
thank for your help
The issue is here
const ResultDetail = (closeResultDetail) => (
You need to destructure it from the props object like this:
const ResultDetail = ({closeResultDetail}) => (
Or use it from props directly like this:
const ResultDetail = (props) => (
<div onClick={props.closeResultDetail}>
...
in your state component
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
....
<ResultCard
....
show={this.state.showResult}
//defer the execution of the method
onClick={(e) => this.showResultDetail(e)}/>
}
resultCard.js
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id, show}) => {
if(show)
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
);
};
in Results you define
showResultDetail = () => {
this.setState( {showResult: false });
};
as a function without arguments. You then pass
showResultDetail={(e) => this.showResultDetail(e)}
as a function with an event argument to your ResultCard. If you change that to
showResultDetail={this.showResultDetail}
your problem might already be fixed.
Edit: Here is a minimal snippet that does what you're looking for, I think.
const ResultCard = ({showResultDetail, show}) => {
return <div className="card" onClick={showResultDetail}>{show?'Click me!':''}</div>
};
class Results extends React.Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
return <ResultCard show={this.state.showResult}
showResultDetail={this.showResultDetail}/>
}
}
ReactDOM.render(<Results/>, document.getElementById('root'))
.card {
width: 200px;
height: 50px;
background: lightgray;
text-align: center;
line-height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>
I'm experiencing some strange activity with my Chatkit app built using React. Essentially, I'm testing with two different users in different rooms. When I send a message from a user in one room. The other user is able to see that message, although they are not in the same room. Here's a screenshot of what's going on.
This only appears to happen when the users have been in the same room at least once.
Buggy Chat
I can tell the messages are being created correctly because I see them in the right place in the ChatKit API. Also, if I re-render the component, the messages end up in the right place. But The cross-room messaging bug still persists.
Corrected Chat
I'm under the impression that it definitely has something to do with the state of the MessageList component. I've made sure to update the component state every time we enter a new room, but I suppose the real question is whether or not other instances of the applications even care about the change in component state for a different instance.
So without further ado, here is my code:
ChatScreen (Main app)
import React from "react"
import Chatkit from "#pusher/chatkit"
import MessageList from "./MessageList"
import SendMessageForm from "./SendMessageForm"
import WhosOnlineList from "./WhosOnlineList"
import RoomList from "./RoomList"
import NewRoomForm from "./NewRoomForm"
import { getCurrentRoom } from "../../actions/chatkitActions"
import { connect } from "react-redux"
class ChatScreen extends React.Component{
constructor(props){
super(props)
this.state = {
messages: [],
currentRoom: {},
currentUser: {},
usersWhoAreTyping: [],
joinableRooms: [],
joinedRooms: [],
errors: {}
}
this.sendMessage = this.sendMessage.bind(this)
this.sendTypingEvent = this.sendTypingEvent.bind(this)
this.subscribeToRoom = this.subscribeToRoom.bind(this)
this.getRooms = this.getRooms.bind(this)
this.createRoom = this.createRoom.bind(this)
}
componentDidMount(){
//setup Chatkit
let tokenUrl
let instanceLocator = "somecode"
if(process.env.NODE_ENV === "production"){
tokenUrl = "somenedpoint"
} else {
tokenUrl = "http://localhost:3000/api/channels/authenticate"
}
const chatManager = new Chatkit.ChatManager({
instanceLocator: instanceLocator,
userId: this.props.chatUser.name,
connectionTimeout: 120000,
tokenProvider: new Chatkit.TokenProvider({
url: tokenUrl
})
})
//initiate Chatkit
chatManager.connect()
.then((currentUser) => {
this.setState({
currentUser: currentUser
})
//get all rooms
this.getRooms()
// if the user is returning to the chat, direct them to the room they last visited
if(this.props.chatkit.currentRoom.id > 0){
this.subscribeToRoom(this.props.chatkit.currentRoom.id)
}
})
}
sendMessage = (text) => {
this.state.currentUser.sendMessage({
roomId: this.state.currentRoom.id,
text: text
})
}
sendTypingEvent = () => {
this.state.currentUser
.isTypingIn({
roomId: this.state.currentRoom.id
})
.catch((errors) => {
this.setState({
errors: errors
})
})
}
getRooms = () => {
this.state.currentUser.getJoinableRooms()
.then((joinableRooms) => {
this.setState({
joinableRooms: joinableRooms,
joinedRooms: this.state.currentUser.rooms
})
})
.catch((errors) => {
this.setState({
errors: { error: "could not retrieve rooms"}
})
})
}
subscribeToRoom = (roomId) => {
this.setState({
messages: []
})
this.state.currentUser.subscribeToRoom({
roomId: roomId,
hooks: {
onNewMessage: (message) => {
this.setState({
messages: [...this.state.messages, message]
})
},
onUserStartedTyping: (currentUser) => {
this.setState({
usersWhoAreTyping: [...this.state.usersWhoAreTyping, currentUser.name]
})
},
onUserStoppedTyping: (currentUser) => {
this.setState({
usersWhoAreTyping: this.state.usersWhoAreTyping.filter((user) => {
return user !== currentUser.name
})
})
},
onUserCameOnline: () => this.forceUpdate(),
onUserWentOffline: () => this.forceUpdate(),
onUserJoined: () => this.forceUpdate()
}
})
.then((currentRoom) => {
this.setState({
currentRoom: currentRoom
})
this.getRooms()
//store currentRoom in redux state
this.props.getCurrentRoom(currentRoom)
})
.catch((errors) => {
this.setState({
errors: errors
})
})
}
createRoom = (roomName) => {
this.state.currentUser.createRoom({
name: roomName
})
.then((newRoom) => {
this.subscribeToRoom(newRoom.id)
})
.catch((errors) => {
this.setState({
errors: { error: "could not create room" }
})
})
}
render(){
const username = this.props.chatUser.name
return(
<div className="container" style={{ display: "flex", fontFamily: "Montserrat", height: "100vh"}}>
<div
className="col-md-3 bg-dark mr-2 p-0"
style={{display: "flex", flexDirection: "column", maxHeight: "80vh", padding: "24px 24px 0px"}}
>
<div style={{flex: "1"}} className="p-4">
<WhosOnlineList users={this.state.currentRoom.users}/>
<RoomList
roomId={this.state.currentRoom.id}
rooms={[...this.state.joinedRooms, ...this.state.joinableRooms]}
subscribeToRoom={this.subscribeToRoom}
/>
</div>
<NewRoomForm createRoom={this.createRoom} user={this.state.currentUser}/>
</div>
<div
className="col-md-9 border p-0"
style={{display: "flex", flexDirection: "column", maxHeight: "80vh"}}
>
<div className="mb-3">
{ this.state.currentRoom.name ? (
<h4
className="bg-black text-light m-0"
style={{padding: "1.0rem 1.2rem"}}
>
{this.state.currentRoom.name}
</h4>
) : (
this.props.chatkit.currentRoom.id > 0 ) ? (
<h3 className="text-dark p-4">Returning to room...</h3>
) : (
<h3 className="text-dark p-4">← Join a Room!</h3>
)}
</div>
<div style={{flex: "1"}}>
<MessageList messages={this.state.messages} room={this.state.currentRoom.id} usersWhoAreTyping={this.state.usersWhoAreTyping}/>
</div>
<SendMessageForm
sendMessage={this.sendMessage}
userTyping={this.sendTypingEvent}
currentRoom={this.state.currentRoom}
/>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return{
chatkit: state.chatkit
}
}
const mapDispatchToProps = (dispatch) => {
return{
getCurrentRoom: (currentRoom) => {
dispatch(getCurrentRoom(currentRoom))
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(ChatScreen)
MessageList (component)
import React from "react"
import ReactDOM from "react-dom"
import TypingIndicator from "./TypingIndicator"
class MessageList extends React.Component{
constructor(props){
super(props)
this.state = {
currentRoom: {}
}
}
componentWillReceiveProps(nextProps){
if(nextProps.room){
console.log(nextProps.room)
this.setState({
currentRoom: nextProps.room
})
}
}
componentWillUpdate(){
const node = ReactDOM.findDOMNode(this)
//scrollTop is the distance from the top. clientHeight is the visible height. scrollHeight is the height on the component
this.shouldScrollToBottom = node.scrollTop + node.clientHeight + 100 >= node.scrollHeight
}
componentDidUpdate(){
//scroll to the bottom if we are close to the bottom of the component
if(this.shouldScrollToBottom){
const node = ReactDOM.findDOMNode(this)
node.scrollTop = node.scrollHeight
}
}
render(){
const messages = this.props.messages
let updatedMessages = []
for(var i = 0; i < messages.length; i++){
let previous = {}
if(i > 0){
previous = messages[i - 1]
}
if(messages[i].senderId === previous.senderId){
updatedMessages.push({...messages[i], senderId: ""})
} else{
updatedMessages.push(messages[i])
}
}
return(
<div>
{this.props.room && (
<div style={{overflow: "scroll", overflowX: "hidden", maxHeight: "65vh"}}>
<ul style={{listStyle: "none"}} className="p-3">
{updatedMessages.map((message, index) => {
return (
<li className="mb-1" key={index}>
<div>
{message.senderId && (
<span
className="text-dark d-block font-weight-bold mt-3"
>
{message.senderId}
</span>
)}
<span
className="bg-info text-light rounded d-inline-block"
style={{padding:".25rem .5rem"}}
>
{message.text}
</span>
</div>
</li>
)
})}
</ul>
<TypingIndicator usersWhoAreTyping={this.props.usersWhoAreTyping}/>
</div>
)}
</div>
)
}
}
export default MessageList
RoomList (component)
import React from "react"
class RoomList extends React.Component{
render(){
const orderedRooms = [...this.props.rooms].sort((a, b) => {
return a.id - b.id
})
return(
<div>
{ this.props.rooms.length > 0 ? (
<div>
<div className="d-flex justify-content-between text-light mb-2">
<h6 className="font-weight-bold">Channels</h6><i className="fa fa-gamepad"></i>
</div>
<ul style={{listStyle: "none", overflow: "scroll", overflowX: "hidden", maxHeight: "27vh"}} className="p-2">
{orderedRooms.map((room, index) => {
return(
<li key={index} className="font-weight-bold mb-2">
<a
onClick={() => {
this.props.subscribeToRoom(room.id)
}}
href="#"
className={room.id === this.props.roomId ? "text-success": "text-info"}
style={{textDecoration: "none"}}
>
<span className="mr-2">#</span>{room.name}
</a>
</li>
)
})}
</ul>
</div>
) : (
<p className="text-muted p-2">Loading...</p>
)}
</div>
)
}
}
Here's the component (ChannelsContainer) that's rendering the ChatScreen as well
import React from "react"
import UsernameForm from "./UsernameForm"
import ChatScreen from "./ChatScreen"
import { connect } from "react-redux"
class ChannelsContainer extends React.Component{
constructor(props){
super(props)
this.state = {
chatScreen: false
}
}
componentWillMount(){
if(this.props.chatkit.chatInitialized){
this.setState({
chatScreen: true
})
}
}
componentWillReceiveProps(nextProps){
if(nextProps.chatkit.chatInitialized){
this.setState({
chatScreen: true
})
}
}
render(){
let chatStage
if(this.state.chatScreen){
chatStage = <ChatScreen chatUser={this.props.chatkit.chatUser}/>
} else{
chatStage = <UsernameForm/>
}
return(
<div style={{minHeight: "90vh"}}>
{chatStage}
</div>
)
}
}
const mapStateToProps = (state) => {
return{
chatkit: state.chatkit
}
}
export default connect(mapStateToProps)(ChannelsContainer)
Please let me know what you guys think.
Fixed. All I had to do was compare room id of the message against the current room id. If they're the same, then I'll update my component state messages field.
onNewMessage: (message) => {
if(message.room.id === this.state.currentRoom.id){
this.setState({
messages: [...this.state.messages, message]
})
}
}
The purpose of implementing <a className="button" onClick={this.loadMore}>Load more news</a> button is to take more objects with API and show without refresh the page. Still not sure the way to implementing setState method is ideal or not
this.setState({
newsData: [...this.state.newsData, ...responseJson]
})
App.js
import React from 'react';
import { Newslist } from './newslist/Newslist';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1,
newsData: ''
}
}
componentDidMount() {
this.page = 1;
this.requestNews();
}
requestNews () {
console.log('koooy');
fetch('http://localhost:3000/api/?page='+this.page)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
newsData: [...this.state.newsData, ...responseJson]
})
})
.catch((error) => {
console.error(error);
});
}
loadMore = () => {
this.requestNews();
}
render() {
return (
<main className="main">
<h1>Hello mate !</h1>
<Paggination />
{ this.state.newsData.length
? <Newslist currentNews={this.state.newsData} loadMoreData={this.loadMore} />
: <p>Loading...</p>
}
</main>
);
}
}
export default App;
Newslist.js
import React from 'react';
export class Newslist extends React.Component {
loadMore = () => {
event.preventDefault();
this.props.loadMoreData();
}
render () {
const newsInList = this.props.currentNews.map(newsDetails => {
return (
<section className="media" key={newsDetails.id}>
{newsDetails.image && <figure className="media-figure">
<img src={newsDetails.image} />
</figure>}
<div className="media-body">
<h3 className="media-title">{newsDetails.header}</h3>
<p>{newsDetails.content}</p>
</div>
</section>
);
});
return (
<div>
{newsInList}
<a className="button" onClick={this.loadMore}>Load more news</a>
</div>
);
}
}
What you have done seems reasonable. Basically, make sure you know your current news page/offset. When you make the API request, send the page/offset with the request and append the new use to the head or tail of the array.
I noticed a suggestion about the usage of Redux, Redux is rather complicated and this is a very simple issue, no need for it here.
I am using react-stripe-elements to create a token for payments. However, according to the documentation when the card form is wrapped in the Elements component it should automatically pickup which stripe elements to tokenize.
However, in this case we are presented with the error
You must provide a Stripe Element or a valid token type to create a Token.
Here is the code:
import React from 'react';
import {CardCVCElement, CardExpiryElement, CardNumberElement, PostalCodeElement, StripeProvider, Elements} from 'react-stripe-elements';
class CheckoutForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(ev) {
ev.preventDefault();
this.props.stripe.createToken({email: 'test#test.com'}).then(({token }) => {console.log('Received Stripe token:', token)});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Card details
<CardNumberElement />
<CardExpiryElement />
<CardCVCElement />
<PostalCodeElement />
</label>
<button>Confirm order</button>
</form>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = { stripe: null };
}
componentDidMount() {
this.setState({ stripe: window.Stripe('test_key') });
}
render() {
return (
<StripeProvider stripe={this.state.stripe}>
<Elements>
<CheckoutForm stripe={this.state.stripe} />
</Elements>
</StripeProvider>
);
}
}
export default App;
According to the documentation the following should be true:
'Within the context of Elements, this call to createToken knows which Element to tokenize, since there's only one in this group.'
However, this doesn't seem to be the case. I have also tried using the single 'Card Element' and have not found any success in doing so.
It turns out I never managed to solve the issue using react-stripe-elements. I ended using the standard JS version (from the stripe documentation). Here is my current working solution:
import React from 'react';
class CheckoutForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
elements: null,
card: null
};
}
componentWillReceiveProps() {
this.setState({ elements: this.props.stripe.elements() }, () => {
this.setState({ card: this.state.elements.create('card') }, () => {
this.state.card.mount('#card-element');
});
});
}
handleSubmit(ev) {
ev.preventDefault();
this.props.stripe.createToken(this.state.card).then((token) => {
console.log('Received Stripe token:', token);
});
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<div className="row">
<label >
Credit or debit card
</label>
<div id="card-element"/>
<div id="card-errors" role="alert"/>
</div>
<button>Submit Payment</button>
</form>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {stripe: window.Stripe('test_key')};
}
render() {
return (
<CheckoutForm stripe={this.state.stripe}/>
);
}
}
export default App;
In the comments they rightly say you need to use the HOC injectStripe.
The docs for stripe.createToken mention that you need to pass the element you wish to tokenize data from.
Also from the github repo README:
⚠️ NOTE injectStripe cannot be used on the same element that renders the Elements component; it must be used on the child component of Elements. injectStripe returns a wrapped component that needs to sit under but above any code where you'd like to access this.props.stripe.
In my specif case I was using a Mobx store and I needed to handle createToken and my form submission in the same place.
Even though I had a reference to stripe since initialisation it didn't work.
The createToken call needs to come from a component child of Elements and with stripe injected.
I ended up having:
#inject('signupStore')
#observer
class CardInput extends React.Component {
componentDidMount() {
const { signupStore } = this.props;
const handleCard = async name => {
return await this.props.stripe.createToken({ name: name });
};
signupStore.assignHandleCard(handleCard);
}
render() {
return (
<label>
Card details
<CardElement style={{ base: { fontSize: '18px' } }} />
</label>
);
}
}
export default injectStripe(CardInput);
Passing the handler back to the store, and then using it from there.
Part of signupStore:
#action
async submitForm(formValues) {
if (this.stripe && this.handleCard) {
const tokenResponse = await this.handleCard(
`${formValues.firstName} ${formValues.lastName}`
);
runInAction(() => {
console.log('Card token received ', tokenResponse);
if (tokenResponse) {
this.cardToken = tokenResponse.token.id;
formValues.cardToken = this.cardToken;
}
});
const response = await request.signup.submit(formValues);
return response;
}
return null;
}
With the new #stripe/react-stripe-js library it's a bit different. We need to use ElementsConsumer component. Load stripe using loadStripe method and use Elements component to use your form with Stripe.
Here is a basic example.
import { Elements, loadStripe } from "#stripe/react-stripe-js"
const stripePromise = loadStripe(STRIPEKEY)
<Elements stripe={stripePromise}>
<CardForm />
</Elements>
CardForm.js
import {
CardNumberElement,
CardExpiryElement,
CardCvcElement,
ElementsConsumer,
} from "#stripe/react-stripe-js"
const StripeForm = ({ stripe, elements }) => {
const handleSubmit = async () => {
if (!stripe || !elements) {
return
}
const cardNumberElement = elements.getElement(CardNumberElement)
const res = await stripe.createToken(cardNumberElement)
}
return (
<form>
<div>
<label htmlFor="cardNumber">Card Number</label>
<div>
<CardNumberElement />
</div>
</div>
<div>
<label htmlFor="cardName">Card Name</label>
<input
type="text"
name="cardName"
required
placeholder="Please Enter"
pattern="[A-Za-z]"
/>
</div>
<div>
<label htmlFor="expDate">Exp. Date</label>
<div>
<CardExpiryElement />
</div>
</div>
<div>
<label htmlFor="CVC">CVC</label>
<div>
<CardCvcElement />
</div>
</div>
</form>
)
}
const CardForm = () => {
return (
<ElementsConsumer>
{({ stripe, elements }) => (
<StripeForm stripe={stripe} elements={elements} />
)}
</ElementsConsumer>
)
}
export default CardForm
React js it's working for me
Card component , Get error , Card Detail and Generate Token
import React, { useState, useEffect } from "react";
import {loadStripe} from '#stripe/stripe-js';
import {CardElement,Elements,useStripe,useElements} from '#stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_YOUR_STRIPE_KYE');
const CheckoutForm = () => {
const stripe = useStripe();
const elements = useElements();
const handleSubmit = async (event) => {
event.preventDefault();
const {error, paymentMethod} = await stripe.createPaymentMethod({
type: 'card',
card: elements.getElement(CardElement),
});
console.log("paymentMethod",paymentMethod);
console.log("error", error);
if (paymentMethod) {
const cardElement = elements.getElement(CardElement);
let token = await stripe.createToken(cardElement);
console.log(token);
}
};
return (
<div>
<form onSubmit={ handleSubmit }>
<div className="login-box" id="step2" >
<div className="form-row">
<label for="card-element" style={ { color:" #76bbdf" } }>
Credit or debit card
</label>
</div>
<div >
<CardElement
className="StripeElement"
options={{
style: {
base: {
fontSize: '16px',
color: '#424770',
'::placeholder': {
color: '#aab7c4',
},
},
invalid: {
color: '#9e2146',
},
},
}}
/>
</div>
<button name="submintbtn2" className="btn btn-primary" > SUBSCRIBE </button>
</div>
</form>
</div>
)};
const Registration = () => (
<div>
<Elements stripe={stripePromise}>
<CheckoutForm />
</Elements>
</div>
);
export default Registration;