How to edit a form in React and Redux? - javascript

I tried to autofill the input field values. So, I sent the id via params from the Cards component to the EditForm component, and in EditForm, in its componentDidMount(), I used that id and fetched the post by id from the database, got the post from the store, and pulled out title and description. So, I made my form prefilled with values that I received from the store. Now, I'm stuck here. How do I edit this form? With these values that I have set in the value property of an input field, obviously I am unable to type anything because I wanted to make it prefill. Now, I'm thinking my approach was wrong. Please help.
Cards
import React, { Component } from "react"
class Cards extends Component {
handleEdit = _id => {
this.props.history.push(`/post/edit/${_id}`)
}
render() {
const { _id, title, description } = this.props.post
return (
<div className="card">
<div className="card-content">
<div className="media">
<div className="media-left">
<figure className="image is-48x48">
<img
src="https://bulma.io/images/placeholders/96x96.png"
alt="Placeholder image"
/>
</figure>
</div>
<div className="media-content" style={{ border: "1px grey" }}>
<p className="title is-5">{title}</p>
<p className="content">{description}</p>
<button onClick={() => {this.handleEdit(_id)}} className="button is-success">
Edit
</button>
</div>
</div>
</div>
</div>
)
}
}
export default Cards
EditForm
import React, { Component } from "react";
import { connect } from "react-redux";
import { getPost } from "../actions/userActions"
class EditForm extends Component {
componentDidMount() {
const id = this.props.match.params.id
this.props.dispatch(getPost(id))
}
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
})
};
render() {
return (
<div>
<input
onChange={this.handleChange}
name="title"
value={this.props.post.post.title}
className="input"
placeholder="Title"
/>
<textarea
onChange={this.handleChange}
name="desctiption"
value={this.props.post.post.description}
className="textarea"
></textarea>
<button>Submit</button>
</div>
);
}
}
const mapStateToProps = store => {
return store;
};
export default connect(mapStateToProps)(EditForm)

define 2 state title and description and set them after you pulled them. Then update the state via onchange function and pass the state as value to input and textarea

import React, { Component } from "react";
import { connect } from "react-redux";
import { getPost } from "../actions/userActions"
class EditForm extends Component {
constructor() {
super();
this.state = {
title: '',
description: ''
};
componentDidMount() {
const id = this.props.match.params.id
this.props.dispatch(getPost(id))
}
componentWillReceiveProps(nextProps) {
if (nextProps.post !== this.props.post) {
this.setState({
title: nextProps.post.post.title,
description: nextProps.post.post.description,
});
}
}
handleChange = event => {
const { name, value } = event.target;
this.setState({
[name]: value
})
};
render() {
const { title, description } = this.state
return (
<div>
<input
onChange={this.handleChange}
name="title"
value={title}
className="input"
placeholder="Title"
/>
<textarea
onChange={this.handleChange}
name="description"
value={description}
className="textarea"
></textarea>
<button>Submit</button>
</div>
);
}
}
const mapStateToProps = store => {
return store;
};
export default connect(mapStateToProps)(EditForm)

Related

TypeError: this.state.searchResults.map is not a function

The moment i try to enter in the search box its leading to the typeerror. I have declared the searchResults as a state. but its still throwing an error. Am i not calling the state properly? What should i change? is it the map method that i'm writing it ?
Sorry i'm new to react could someone possibly tell what i'm doing wrong thanks.
Search.js
import React, { Component } from 'react';
import './Search.css';
import axios from 'axios';
class Search extends Component {
constructor(props){
super(props);
this.state = {
searchResults : []
}
}
componentDidMount () {
const token = 'Bearer Wookie2019';
axios.get('https://wookie.codesubmit.io/movies?q=<search_term>',{
headers: {
'Authorization': token
}
}).then(response => {
this.setState({movies : response.data.movies});
console.log(response);
});
}
searchHandler = (event) => {
this.setState({
searchResults : event.target.value
})
console.log(this.searchResults);
}
render(){
return (
// <div className="search" >
// <input className = "form-control" type="text" avlue="inputVal" onChange = {this.searchHandler}/>
// {/* <i className="fas fa-search"></i> */}
// <button className ="btn btn-info">Search</button>
// <div className ="searchres">
// </div>
// </div>
<div>
<form>
<input className = "form-control" placeholder = "Search Movies" ref = { input => this.search = input} onChange = {this.searchHandler}/>
</form>
<div className = "searchresults">
{ this.state.searchResults.map(searchResult => <searchResult movie={this.state.searchResults}>{searchResult.title}</searchResult>)}
</div>
</div>
)
}
}
export default Search;
Title.js
import React, { Fragment } from 'react';
import Search from '../Search/Search';
import './Title.css';
const Title = (props) => {
return (
<Fragment>
<div className ="conatiner">
<div className ="row titlehead">
<div className="col-6">
<h3 className = "title">WOOKIE <br/> MOVIES</h3>
</div>
<div className = "col-6 searchMovie">
<Search {...props} />
</div>
</div>
</div>
</Fragment>
)
}
export default Title
Issues
If I had to guess I would say your onChange handler is setting your state to be undefined, or rather, to whatever you are inputting, which is a string, not an array.
Another issue is that you attempt to fetch your data when the component mounts, not when a search is entered.
The search is never invoked after mounting.
Solution
I suggest using the form's onSubmit event to do the search. Provide the input with an id attribute that can be accessed from the onSubmit event object. Pass the entered value to a computed query string.
import React, { Component } from 'react';
import './Search.css';
import axios from 'axios';
class Search extends Component {
constructor(props){
super(props);
this.state = {
searchResults : []
}
}
searchHandler = (event) => {
event.preventDefault();
const searchTerm = event.target.searchTerm.value
const token = 'Bearer Wookie2019';
axios.get(`https://wookie.codesubmit.io/movies?q=${searchTerm}`,{
headers: {
'Authorization': token
}
}).then(response => {
this.setState({ movies: response.data.movies });
console.log(response);
});
};
render(){
return (
<div>
<form onSubmit={searchHandler}>
<input
id="searchTerm"
className="form-control"
placeholder="Search Movies"
/>
<button type="submit">Search</button>
</form>
<div className="searchresults">
{this.state.searchResults.map(searchResult => <searchResult movie={this.state.searchResults}>{searchResult.title}</searchResult>)}
</div>
</div>
)
}
}

Pass data from input field

I want to take data from an input field and pass it into a to Generate QR code. When I pass the value as a text its work. But can’t pass the input value. Does anyone have a simple example of this working?
import React, {Component} from 'react'
import QrCode from 'react.qrcode.generator'
class DemoEdit extends Component {
constructor() {
super();
this.state = {
topicIn: "",
topicOut:""
};
this.handleChange=this.handleChange.bind(this);
this.generate=this.generate.bind(this);
}
handleChange ({ target}){
this.setState({
[target.name] : target.value
})
}
generate(){
this.setState({
topicOut:this.state.topicIn
})
}
render() {
return <div>
<input
type="text"
name="topicIn"
placeholder="Enter Text Here..."
value={this.state.topicIn}
onChange={this.handleChange}
/>
<button value="Send" onClick={this.generate}>Generate</button>
<p>{this.state.topicOut}</p>
<QrCode value={this.state.topicOut}/>
</div>
}
}
export default DemoEdit;
Use a different package. I just tried qrcode.react
import React, { Component } from "react";
import QrCode from "qrcode.react";
class DemoEdit extends Component {
constructor() {
super();
this.state = {
topicIn: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange({ target }) {
console.log("value", target.value);
this.setState({
topicIn: `${target.value}`
});
}
render() {
return (
<div>
<input
type="text"
name="topicIn"
placeholder="Enter Text Here..."
value={this.state.topicIn}
onChange={this.handleChange}
/>
<QrCode value={this.state.topicIn} />
</div>
);
}
}
export default DemoEdit;
or as a more fancy functional component with hooks, which I totally recommend to use
import React, { useState } from "react";
import QrCode from "qrcode.react";
const DemoEdit = () => {
const [topicIn, setTopicIn] = useState("");
const onChange = (event) => setTopicIn(event.target.value);
return (
<div>
<input
type="text"
name="topicIn"
placeholder="Enter Text Here..."
value={topicIn}
onChange={onChange}
/>
<QrCode value={topicIn} />
</div>
);
};
export default DemoEdit;

Why the state is not updating in App.js after setting up this.setState?

Here is my simple to-do app program where I have made only one component which takes in the input form user and passes that input value to App.js to update items in App.js state.
todo-form.component.js
import React from 'react';
class SignInForm extends React.Component {
constructor(){
super();
this.state ={
temp: null
};
}
handleChange = (e) => {
e.preventDefault();
this.setState({
temp: e.target.value
},
console.log(this.state)
);
// this.props.addInput(e.target.value);
}
handleSubmit= (e) => {
e.preventDefault();
console.log(this.state.temp);
this.props.addInput(this.state.temp);
}
render(){
return(
<div className="container-form">
<form onSubmit={this.handleSubmit}>
<input
name="description"
type="text"
placeholder="add description"
onChange={this.handleChange}
value={this.state.input}
/>
<button type="submit">ADD</button>
</form>
</div>
);
}
}
export default SignInForm;
App.js
import React from 'react';
import './App.css';
import SignInForm from './components/todo-form/todo-form.component'
import ItemList from './components/todo-list/todo-list.component';
class App extends React.Component {
constructor(){
super();
this.state = {
input: []
};
}
addInput = (item) => {
let newInput=[...this.state.input,item];
console.log(newInput);
this.setState = ({
input: newInput
},
console.log(this.state)
);
}
render(){
return (
<div className="App">
<h1>TO-DO LIST</h1>
<SignInForm addInput={this.addInput} />
</div>
);
}
}
export default App;
On taking input the state inside todo-form.component.js is getting updated with the typed input value but on passing state.temp in handleChange function, the state inside App.js is not updating when addInput function is called.
Please help me on this issue and how my state is not getting updated in App.js??
Your setState is the problem. Have a look at my code.
App.js
class App extends React.Component {
state = {
input: [],
};
addInput = (item) => {
let newInput = [...this.state.input, item];
//setState should be this way
this.setState({
input: newInput,
});
};
render() {
return (
<div className="App">
<h1>TO-DO LIST</h1>
{this.state.input.map((el) => (
<li> {el}</li>
))}
<SignInForm addInput={this.addInput} />
</div>
);
}
}
export default App;
Login file.
class SignInForm extends React.Component {
// constructor(props) {
// super(props);
state = {
temp: null,
};
// }
handleChange = (e) => {
e.preventDefault();
this.setState({
temp: e.target.value,
});
// this.props.addInput(e.target.value);
};
handleSubmit = (e) => {
e.preventDefault();
console.log(this.state.temp);
this.props.addInput(this.state.temp);
};
render() {
return (
<div className="container-form">
<form onSubmit={this.handleSubmit}>
<input
name="description"
type="text"
placeholder="add description"
onChange={this.handleChange}
value={this.state.input}
/>
<button type="submit">ADD</button>
</form>
</div>
);
}
}
export default SignInForm;

In react, how to get value stored in draft.js to show up in another component?

I am new to react and having hard time figuring on how to show up the value from another component to the edit file component. Basically, I am able to create a message using draft.js rich text editor and able to save it to the database in the backend. Now I need to work on the update/edit part. How do I get the description state value to show up in the rich text editor? Can anyone please help?
Create File
import { EditorState } from 'draft-js';
class CreateModal extends Component {
constructor(props) {
super(props);
this.state = {
description: EditorState.createEmpty()
};
this.handleEditorState = this.handleEditorState.bind(this);
}
handleEditorState(editorState) {
this.setState({
description: editorState
})
}
render() {
const { release } = this.props.state;
return (
<div>
...
<CreateForm
description={this.state.description}
handleEditorState={this.handleEditorState}
handleSubmit={(e) => handleSubmit(e)}
/>
...
);
}
}
Form file
import { Editor } from 'react-draft-wysiwyg';
import '../../react-draft-wysiwyg.css';
class CreateForm extends Component {
render() {
const { description, handleEditorState, handleSubmit } = this.props;
return (
<div>
<form className="form-horizontal" onSubmit={handleSubmit}>
<Editor
editorState={description}
onEditorStateChange={handleEditorState}
/>
{/* Buttons */}
<div className={`modal-footer ${styles.modalFooter}`}>
...
</div>
</form>
</div>
);
}
}
Edit file
class EditAccessInstructionModal extends Component {
constructor(props) {
super(props);
this.state = {
description: EditorState.createEmpty()
}
}
componentWillMount(){
this.setState({
description: this.props.accessInstructions.description
});
}
componentWillReceiveProps(nextProps) {
this.setState({
description: nextProps.accessInstructions.description
});
}
onDescriptionChange(event) { this.setState({ description: event.target.value }); }
render() {
const { description } = this.state;
return (
<div className="btn-group">
<div className={`modal-body ${styles['proj-modal-body']}`}>
<form id="modalForm" className="form-horizontal">
<Editor
value={description}
/>
</form>
</div>
</div>
)
}
}

react-stripe-elements Error: You must provide a Stripe Element or a valid token type to create a Token

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;

Categories