Hello guys I'm pretty new at React js and I just started using the react-stripe-js. My question is, is it possible to make stay the value in Stepper Elements in React Stepper after changing page? Your answers are very much appreciated.
Sample Design Stepper with Stipe Elements
class CheckoutForm extends React.Component {
handleSubmit = (ev) => {
ev.preventDefault();
this.props.stripe
.createPaymentMethod('card')
.then((payload) => {
console.log('[pm]', payload)
});
this.props.stripe
.createToken({type: 'card', name: 'Jenny Rosen'})
.then((payload) => {
console.log(payload)
});
this.props.stripe.createSource({
type: 'card',
owner: {
name: 'Jenny Rosen',
},
});
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<CardSection />
<button>Confirm order</button>
</form>
);
}
}
//CardSection.js
class CardSection extends React.Component {
render() {
return (
<>
<label>
Name
<input name="name" type="text" placeholder="Jane Doe" required />
</label>
<label>
Card details
<CardNumberElement style={{base: {fontSize: '18px'}}}/>
<CardExpiryElement style={{base: {fontSize: '18px'}}} />
<CardCVCElement style={{base: {fontSize: '18px'}}} />
</label>
</>
);
}
}
The state is local to your component. Whatever logic you have in next() appears to be selectively rendering depending on which step the user has reached.
This is a problem because when the user moves to the next step, your state values is unmounted and destroyed and therefore loses its state.
The solution is to save values as a prop on your child component, and move the handleChange up into the parent component and have that as a prop on Child Component as well. Store values in the state of the parent component which doesn't unmount on change of step.
In your parent component, put the handleChange event so it stores in the parent state.
Now, as the user moves to the next screen, you have safely stored the selected values in the parent state.
Related
Setup: I have set up a two react components in a parent child relationship. The parent has a state that can be changed by press of a button on parent itself.
Expected behaviour: In the child, I have an input field and I want the state to change to the value I send in the input field on the press of the submit button. I have set up the parent and the child as follows:
What I have tried: I going through this answer and this youtube video but I guess I am not smart enough to make sense of it.
This is what my code looks like
Parent:
class App extends Component {
state = {
value:"someValue"
};
changeValue = (value) => {
this.setState({
value
})
}
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={()=>this.changeValue("valueFromParentComponent")}>Press to change value from parent component</button>
<br/><br/>
<Child getChildInputOnSubmit={()=>this.changeValue()} />
</div>
);
}
}
And this is what the child looks like
Child:
class Child extends Component {
state = {
}
sendChildData = (childInputValue) => {
console.group("This is the data from the input of the child component")
console.log("==============")
console.log(childInputValue)
console.log("==============")
}
render() {
return (
<div>
This is the child component
<br /><br />
<form>
<input type="text" placeholder="Some placeholder"></input>
<button onSubmit={this.sendChildData()} type="submit">Send child's input to parent</button>
</form>
</div>);
}
}
The React behaviour encourages to implement an inverse data flow inside a component hierarchy. Meaning that the child components can receive parent methods through props, this methods will work as callbacks, allowing to receive data, trigger behaviours, update his state and more.
I attach a StackBlitz example, showing how this concept would work in your setup https://stackblitz.com/edit/react-jsv5jo
Edit: Here a few extra tips applied on the example:
To work with inputs on React, a common setup consists on listen the onChange event to receive new data and update the component state. Then, this state is used in the value attribute to update the input content on DOM.
Listen the onSubmit event on the form tag instead on submit button, and remember to add some logic to avoid reloading.
Another good practice on React components is initialize your state object inside the Constructor (In case to be working with a Class Component) and write methods to avoid bloat the render one (Be sure to bind the extra methods on your constructor to avoid invocation problems)
Callbacks are used to pass data from Child component to Parent component in React.
We wright function in Parent component that will receive value and pass this function to child component through Props.
class Parent extends Component {
state = {
value: 'someValue'
};
changeValue = value => {
this.setState({
value
});
};
render() {
return (
<div>
<p>this is the value from state: {this.state.value}</p>
<button onClick={() => this.changeValue('valueFromParentComponent')}>
Press to change value from parent component
</button>
<br></br>
<Child getChildInputOnSubmit={this.changeValue} />
</div>
);
}
}
Now in Child component we call Parents function that we passed in props and send value.
class Child extends Component {
constructor(props) {
super(props);
this.state = {
Childvalue: ''
};
}
handleChange = event => {
event.preventDefault();
this.setState({ Childvalue: event.target.value });
};
sendToParent = () => {
//here calling Parents changeValue
this.props.getChildInputOnSubmit(this.state.Childvalue);
};
render() {
return (
<div>
This is the child Component
<br></br>
<form action='#' onSubmit={this.sendToParent}>
<input
type='text'
placeholder='Some placeholder'
value={this.state.Childvalue}
onChange={this.handleChange}
></input>
<button type='submit'>Send child's input to parent</button>
</form>
</div>
);
}
}
I am new to React ,
I tried to design a form using material UI in React.
I was able to design the form with textfields but unable to edit the data if i use value attribute to the textfield.
And how can i call a parent function when onChange function is called for Textfield which is there in child component.Here is my Code.
In the parent Component i am including like this
render() {
const { name, email, mobileNumber } = this.state.serviceRequest;
return (
<div>
<HomeTemplate
handleShow = {this.handleShow}
handleClose = {this.handleClose}
name = {name}
email ={email}
mobileNumber = {mobileNumber}
DateFnsUtils ={DateFnsUtils}
handleDateChange ={this.handleDateChange}
handleChange = {this.handleChange}
/>
</div>
);
}
In the child component i am having Text field like this. due to unable to post the whole code i am posting part of the code which is useful to get the problem.
I will post paste bin link in comments also.
<TextField
autoFocus
margin="dense"
id="emailId"
label="Email Address"
type="email"
value= {props.email}
fullWidth
/>
Please suggest me how can i do this?
You can send a function of Parent as a prop to the Child and set that as onChange prop of TextField.
For example, let's say your Child component looks like this:
function Demo(props) {
return (
<TextField
fullWidth
id="standard-name"
label="Name"
value={props.name} // it gets value from prop
onChange={props.onNameChange} // it gets handler function from prop too!
margin="normal"
/>
);
}
Now your parent component is responsible to send both props.name and props.onNameChange:
class App extends React.Component {
state = {
name: "Sleepy cat"
};
handleNameChange = event => {
this.setState({ name: event.target.value });
};
render() {
return (
<Demo
onNameChange={this.handleNameChange} // send a function as prop, that will change the state in parent
name={this.state.name} // send the state of parent to child
/>
);
}
}
Here is the full demo:
const {TextField} = window['material-ui'];
function Demo(props) {
return (
<TextField
fullWidth
id="standard-name"
label="Name"
value={props.name}
onChange={props.onNameChange}
margin="normal"
/>
);
}
class App extends React.Component {
state = {
name: "Sleepy cat"
};
handleNameChange = event => {
this.setState({ name: event.target.value });
};
render() {
return (
<div>
<code>{"Parent State: " + JSON.stringify(this.state)}</code>
<Demo onNameChange={this.handleNameChange} name={this.state.name} />
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/#material-ui/core#3.9.2/umd/material-ui.production.min.js"></script>
<div id="root"></div>
What you want to achieve is a common need a developer requires when building application using react.
You want the child component to the reflect the changing data when calling onChangeevent.
Note, the child component can have its own state but in case there are several components that need to reflect the same changing data you can lift the state up to the closest ancestor.
#mehamasun has already illustrated how to do that in his example.
Parent(state) -> Child(props) -> //Event called -> Parent(state changed via event handler) -> rerender Child(props)
I have a Field Component (redux-form) that calls a custom ImageInput component that uploads an image, then spits out the src.
I need to pass that src from my child component, to the parent where it updates the parents state via a handler.
I've tried to follow this answer, but am a bit confused as my setup seems a little different: How to update parent's state in React?
Here is my parent component
handleChange(e) {
console.log(e)
this.setState({[e.target.name]: e.target.value});
}
<Field
{...this.props}
component={ImageInput}
name="templating.img"
onChange={this.handleChange.bind(this)}
/>
and then my child where the image is inputted and uploaded
constructor(props) {
console.log('props', props);
super(props);
this.state = {
src: null
}
console.log(this.state)
}
_imgUpload = (e) => {
e.preventDefault();
// console.log(e.target.files)
if (e.target.files.length === 1) {
let file = e.target.files[0]
loadImage(e.target.files[0])
.then(uploadThumbor)
.then(formatImage)
.then(({src, dataUri}) => {
this.setState({src: src})
console.log('img', src)
})
}
}
/* Snippet where the image upload occurs */
<div style={styles.image}>
<div style={styles.defaultPreview}></div>
<div style={styles.sample}></div>
<input type="file" style={styles.uploadPreview} accept="image/*" onChange={this._imgUpload} />
</div>
After an image has been upload, I am setting the state in the child component. I need to pass that state to the parent where it'll update it's state. I dont think the onChange={this.handleChange.bind(this)} is correct for this parent component in this instance (it is correct for other Field components that are simple inputs).
Any help would be awesome.
I get what you are trying to do.
You said that you are using redux form. Is this the only input in the form? Why is it uploading the image separately as soon as it changes?
I ask this cause the call to the api is normally dispatched form the parent container. If this api call is specific to this page I would suggest moving it to the parent and maintain the src state in the parent. If you need it here you can always pass it down as prop.
If this is not an option you can always have a separate callback function similar to the handleOnChange. It would be called when you get the new src from the backend. This will allow you to maintain the src in the parent state as you wanted. In this case, if you need to have the src in the input component you can do the same as in the other solution, pass it down as a prop.
Parent
handleChange(e) {
console.log(e)
this.setState({[e.target.name]: e.target.value});
}
handleSrcChange(src) {
this.setState({ imageSrc: src });
}
<Field
{...this.props}
component={ImageInput}
name="templating.img"
onChange={this.handleChange.bind(this)}
onSrcChange={this.handleSrcChange.bind(this)}
/>
Input Component
_imgUpload = (e) => {
e.preventDefault();
// console.log(e.target.files)
if (e.target.files.length === 1) {
let file = e.target.files[0]
loadImage(e.target.files[0])
.then(uploadThumbor)
.then(formatImage)
.then(({src, dataUri}) => {
this.props.onSrcChange(src);
console.log('img', src)
})
}
}
/* Snippet where the image upload occurs */
<div style={styles.image}>
<div style={styles.defaultPreview}></div>
<div style={styles.sample}></div>
<input type="file" style={styles.uploadPreview} accept="image/*" onChange={this._imgUpload} />
</div>
I have a component in a React class in my Laravel project which is a simple form with one input field. It houses a phone number which I have retrieved from the database and passed back through the reducer and into the component as a prop. Using this, I have passed it through to the module as a prop which then populates the field with the currently saved value:
<OutOfOfficeContactNumberForm
show={props.showOutOfOffice}
value={props.outOfOfficeNumber}
handleChange={console.log("changed")}
/>
I have a handleChange on here which is supposed to fire a console log, but it only ever displays on page load. Here is my form module class:
class OutOfOfficeContactNumberForm extends React.Component {
render() {
const { show, value, handleChange } = this.props;
if(!show) return null;
return (
<div>
<p>
Please supply an Out of Office contact number to continue.
</p>
<InputGroup layout="inline">
<Label layout="inline" required={true}>Out of Office Contact Number</Label>
<Input onChange={handleChange} value={value} layout="inline" id="out-of-office-number" name="out_of_office_contact_number" />
</InputGroup>
</div>
);
}
}
export default (CSSModules(OutOfOfficeContactNumberForm, style));
The form is embedded in my parent component, as follows:
return (
<SectionCategoriesSettingsForm
isSubmitting={this.state.isSubmitting}
page={this.props.page}
show={this.props.show}
categories={this.props.categories}
submitSectionCategoriesSettings={this._submit.bind(this, 'add')}
updateSelectedCategories={this._updateSelectedCategories.bind(this)}
selectedCategoryIds={this.state.selectedCategoryIds}
storedUserCategories={this.props.selectedCategories}
outOfOfficeNumber={this.state.outOfOfficeNumber}
onUpdateContactNumber={this._updateContactNumber.bind(this)}
/>
);
In my componentWillReceiveProps() function, I set the state as follows:
if (nextProps.selectedCategories && nextProps.selectedCategories.length > 0) {
this.setState({
outOfOfficeNumber: nextProps.outOfOfficeNumber,
selectedCategoryIds: nextProps.selectedCategories.map(c => c.id)
});
}
I'm pretty sure the reason it's not changing is because it's pre-loaded from the state which doesn't change - but if I cannot edit the field how can I get it to register a change?
EDIT: Just to clarify there are also checkboxes in this form for the user to change their preferences, and the data retrieved for them is set the same way but I am able to check and uncheck those no problem
Changes:
1- onChange expect a function and you are assigning a value that's why, put the console statement inside a function and pass that function toOutOfOfficeContactNumberForm component , like this:
handleChange={() => console.log("changed")}
2- You are using controlled component (using the value property), so you need to update the value inside onChange function otherwise it will not allow you to change means input values will not be not reflect in ui.
Check example:
class App extends React.Component {
state = {
input1: '',
input2: '',
}
onChange = (e) => this.setState({ input2: e.target.value })
render() {
return(
<div>
Without updating value inside onChange
<input value={this.state.input1} onChange={console.log('value')} />
<br />
Updating value in onChange
<input value={this.state.input2} onChange={this.onChange} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app' />
I think the best way is when you get data from database put it to state and pass the state to input and remember if you want to see input changes in typing, use a function to handle the change and that function should change state value.
class payloadcontainer extends Component {
constructor(props) {
super(props)
this.state = {
number:1
}
}
render() {
return (
<div>
<input value={this.state.number} onChange={(e)=>this.setState({number:e.target.value})}></input>
<button onClick={()=>this.props.buyCake(this.state.number)}><h3>buy {this.state.number} cake </h3></button>
</div>
)
}
}
In my case I try to create a simple Form Component - mostly for "testing" reactjs and work with it.
To do this I work with 2 Components. The first Component is the Parent, the "Form" Component. The second Component is the field of the form - for example a simple textfield. This is the markup it would look like:
<Form
schema={MyFormSchema}
>
<Input name="name" />
<Input name="age" />
</Form>
In MyFormSchema I have all information which I need for every Child of the type "Input". For this case I have done this in my "Form" Component:
Form.jsx
Form = React.createClass({
renderChildren() {
return React.Children.map(this.props.children, (child)=>{
if (child.type && child.type.prototype && child.type.prototype.constructor.displayName === 'Input') {
let additionalProps = {
fieldSchema: this.props.schema.pick(child.props.name),
onChange: this.onChange
};
return React.cloneElement(child, additionalProps);
}
return child;
});
},
render() {
return(
<form>
{this.renderChildren()}
</form>
);
}
});
What I am doing here is to "clone" every "input" child and add some new props depending on the schema.
So the first question is:
Is this really the correct war in reactJs ? When I am not "cloning" every element and adding new properties I have to add the property directly in my View, right ? Something like but I am trying to prevent this because all information I need I already have as a prop in my Form Schema.
After playing around with this I found out, that this.props.children only have the first level of children. But when I have nested my Children in my Form Component it will not work anymore that my Component is replacing the Input Component with the manipulated component.
Example:
<Form
schema={MyFormSchema}
>
<AnotherComponent>
<Input name="name" />
</AnotherComponent>
<Input name="age" />
</Form>
When I am doing it like I now doing it this code will not work anymore because in this.props.children I only have [AnotherComponent, Input[name=age]] and the Input[name=name] is missing. So I think the way I am doing it is the wrong way. But i am not sure.
So the main question is:
Like in my example: What is the correct way in ReactJs to inherit props (or what ever) to all children (also the nested one) - or is this not possible in the "react" way and I really have to pass all necessary props to all children ?
Edit:
When I am talking about "pass all necessary props to all children" I mean something like this:
<Form
schema={MyFormSchema}
>
<AnotherComponent>
<Input name="name" fieldSchema={this.getFieldSchema('name')} onChange={this.onChange} />
</AnotherComponent>
<Input name="age" fieldSchema={this.getFieldSchema('age')} onChange={this.onChange} />
</Form>
In this example I would pass all necessary props I want to add dynamically by the parent. In my example above the next problem would be: "this" would not work for the name input because of its parent AnotherComponent. So I would have to reference to the parent - of course: its possible, but I think it would be a ugly way.
There are three correct ways to deeply pass props:
1) Just actually pass them down the tree from each component to the next (this is the most readable (in terms of code logic), but can get unwieldy once you have too many props to pass and lots of levels in your tree.
Example:
import React from 'react';
var GrandParent = React.createClass({
render () {
return (
<Parent familyName={'componentFamily'} />
);
}
});
var Parent = React.createClass({
render () {
return (
<Child familyName={props.familyName} />
);
}
});
var Child = React.createClass({
render () {
return (
<p>{'Our family name is ' + props.familyName}</p>
);
}
});
2) Use a Flux-style store (I prefer Reflux, though Redux is all the rage right now) to keep a common state. All components can then access that store. For me at least, this is the current preferred method. It's clear and it keeps business logic out of the components.
Example (using Reflux):
import React from 'react';
import Reflux from 'reflux';
var MyStore = Reflux.createStore({
// This will be called in every component that connects to the store
getInitialState () {
return {
// Contents of MyFormSchema here
};
}
});
var Input = React.createClass({
propTypes: {
name: React.PropTypes.string.isRequired
},
mixins: [Reflux.connect(MyStore)],
render () {
// I don't know what MyFormSchema so I'm generalizing here, but lets pretend its a map that uses the name of each field a key and then has properties of that field within the map stored at the key/value
return (
<input type={this.state[name].type} name={this.props.name} value={this.state[name].type} />
);
}
});
3) Use React's context feature. As you'll see immediately from looking at the docs, this feature is still in development and is subject to possible change and even removal in future versions of React. So, while it is likely the easiest way to pass props down a tree of components, personally I'm staying away from it until it becomes more of a finalized feature.
I'm not going to write an example for this one since the docs make it very clear. However, make sure to scroll down on the doc page and take a look at Parent-child coupling, which is kind of what you're doing right now.
Another solution for you is that instead of having a single component that renders Form and its Inputs, why not just pass the prop to Form as you do currently, and then simply render the individual Input using Form's render().
You could use react-addons-clone-with-props package this way:
import React, { Component } from 'react';
import cloneWithProps from 'react-addons-clone-with-props';
// ...
class Form extends Component {
recursivelyMapChildren(children) {
return React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
return React.cloneElement(child, {
...child.props,
children: this.recursiveCloneChildren(child.props.children)
});
})
}
render() {
return (
<form>{this.recursivelyMapChildren(this.props.children)}</form>
);
}
}
What the code does:
Gets all the children components via predefined children prop (see docs).
Recursively maps the collection of children with React.Children.map method, applying a lambda function to each element.
Saves the mapped (i.e. updated, but not mutated!) children elements into mappedChildren constant.
Puts them within form DOM element.
It looks simple and it should be so.
But you have to keep in mind that React is great when your code is kept clean and transparent. When you explicitly pass props like
<Form
schema={MyFormSchema}
>
<Input
name="name"
schema={MyFormSchema} />
<Input
name="age"
schema={MyFormSchema} />
</Form>
there's way less things to get broken when you accidentally change the underlying logic.
Thankyou. Credits #Rishat Muhametshin
I have used the above to create a re-usable method.
This works beautifully:
utils/recursivelyMapChildren.jsx
const recursivelyMapChildren = (children, addedProperties) => {
return React.Children.map(children, child => {
if (!React.isValidElement(child)) {
return child;
}
return React.cloneElement(child, {
...child.props,
...addedProperties,
children: this.recursivelyMapChildren(child.props.children, addedProperties)
});
})
};
export default recursivelyMapChildren;
usecase:
Form.jsx
import recursivelyMapChildren from 'utils/recursivelyMapChildren';
class Form extends Component {
handleValidation(evt, name, strValidationType){
/* pass this method down to any nested level input field */
}
render(){
return (
<form>
{recursivelyMapChildren(this.props.children, {
handleValidation: this.handleValidation.bind(this)
})}
<input type="submit" value="submit" className="validation__submit"/>
</form>
)
}
}
export default Form
SomeExample.jsx
const SomeExample = () => {
return (
<Form>
<input type="hidden" name="_method" value="PUT"/>
<fieldset>
<legend>Personal details</legend>
<div className="formRow">
<InputText/> {/* This component will receive the method - handleValidation, so it is possible to update the state on the nested grand parent - form */}
</div>
<div className="formRow">
<InputText/>{/* This component will receive the method - handleValidation, so it is possible to update the state on the nested grand parent - form */}
</div>
</fieldset>
</Form>
)
}
export default SomeExample;
I have an alternate solution to pass props to nested children. The function createFormComponents takes the schema and produces an object of components that will receive props as usual but with the schema already provided. You could link the FormContainer in my example up to a store or use setState to handle changes to the schema over time and the children will update correctly.
The example's output is to the console to demonstrate that the props are received as expected.
function Form_(props) {
console.log('Form props', props)
return <div>{props.children}</div>
}
function Input_(props) {
console.log('Input props', props)
return <div />
}
function createFormComponents(schema) {
return {
Form: props => {
return Form_({ ...props, schema })
},
Input: props => {
return Input_({ ...props, schema })
},
}
}
const FormContainer = React.createClass({
render: function() {
const myFormSchema = { x: 0, y: 1, z: 2 }
const {
Form,
Input,
} = createFormComponents(myFormSchema)
return (
<Form>
<Input name="name" />
<Input name="age" />
</Form>
)
}
})
ReactDOM.render(
<FormContainer />,
document.getElementById('container')
)
Fiddle: Props Example