I am new to React and I'm not sure what would be the best approach to take.
I have a modal component to be displayed once the user fills out the values inside the form and click on Preview Voucher to print those values inside the modal.
I tried this code and below I have the Preview Voucher component with a constructor and events.
// PreviewVoucher.js
class PreviewVoucher extends React.Component {
constructor(props) {
super(props);
this.state = {
voucherNumber: "0",
//
addModalShow: false
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleSelectChange = this.handleSelectChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleInputChange(e) {
const target = e.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const inputName = target.name;
this.setState({
[inputName]: value
});
}
handleSelectChange(e) {
this.setState({ labelSize: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
}
}
And I want to render this form on the page that has a child component Modal Voucher
// PreviewVoucher.js
render() {
let addModalClose = () => this.setState({ addModalShow: false });
return (
<form onSubmit={this.handleSubmit}>
<div>
<p>Number of vouchers to create:</p>
<input
min="0"
type="number"
name="voucherNumber"
value={this.state.voucherNumber}
onChange={this.handleInputChange}
/>
</div>
<div>
<h4>Message:</h4>
<p>Some message</p>
<ButtonToolbar>
<Button onClick={() => this.setState({ addModalShow: true })}>
Preview Voucher
</Button>
<ModalVoucher
show={this.state.addModalShow}
onHide={addModalClose}
/>
</ButtonToolbar>
</div>
<input type="submit" value="Create voucher" />
</form>
);
}
And this is the child component - Modal Voucher that would contain some text and would like to display the dynamic values from the Preview Voucher component inside the < Modal Body >.
// ModalVoucher.js
class ModalVoucher extends React.Component {
render() {
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Voucher preview
</Modal.Title>
</Modal.Header>
<Modal.Body>
<div>{/* update code here */}</div>
</Modal.Body>
<Modal.Footer>
<Button onClick={this.props.onHide}>Close</Button>
</Modal.Footer>
</Modal>
);
}
}
How to render parent's state within child component
You will need to pass the state value from PreviewVoucher into its child ModalVoucher as a prop.
// PreviewVoucher.js
render() {
<ModalVoucher
show={this.state.addModalShow}
onHide={addModalClose}
voucherNumber={this.state.voucherNumber}
/>
}
Then use it inside of ModalVouchers render method.
// ModalVoucher.js
render() {
<Modal.Body>
<div>{this.props.voucherNumber}</div>
</Modal.Body>
}
I put together this sandbox showing this in action.
Other suggestions
After making this change, you might see this error in the console
Warning: React does not recognize the `voucherNumber` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `vouchernumber` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
This occurs because you are passing all the props directly into the Modal, which
the underlying library passes to a DOM element.
<Modal
{...this.props}
Because of this, I think it is usually a bad idea to directly forward all props
and I prefer to pass specific props instead.
<Modal
show={this.props.show}
onHide={this.props.onHide}
Related
I am new to React and web dev in general.
I have created a Component containing list of calculator like buttons which is stored in Buttons.js.
There is another component called as Submit stored in Submit.js. Submit component is basically a textbox in which we type a mathematical expression which I want to process later.
Both of these components are then called in another component call Leftwindow.js.
So my question is,
How can I make clicking in Buttons component affect the textbox in Submit component. I know it could be done easily had the buttons and input box been the part of a single component.
Basically if I press the '1' button I want it to be added to the input box.
A snapshot of how it looks -
Overview
Code for Buttons.js -
class Buttons extends Component {
constructor(props){
super(props);
this.state = {
//buttonrows//
};
}
render(){
const row1elems = this.state.row1.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row2elems = this.state.row2.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row3elems = this.state.row3.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
const row4elems = this.state.row4.map((button) => {
return (
<Button color={colours[button.type]} className="buttonsize">{button.label}</Button>
);
});
return (
<div className="center">
<ButtonGroup>
{row1elems}
</ButtonGroup>
<ButtonGroup>
{row2elems}
</ButtonGroup>
<ButtonGroup>
{row3elems}
</ButtonGroup>
<ButtonGroup>
{row4elems}
</ButtonGroup>
</div>
);
}
}
export default Buttons;
Code for Submit.js -
class Submit extends Component{
constructor(props){
super(props);
this.state = {
fx: ''
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event){
const target = event.target;
const val = target.val;
const name = target.name;
this.setState({
[name]: val
})
}
handleSubmit(event){
}
render(){
return(
<div>
<Form onSubmit={this.handleSubmit}>
<FormGroup row>
<Col md={12}>
<Input type="text" id="fx" name="fx" placeholder="Type Here" value = {this.state.fx} onChange={this.handleInputChange} />
</Col>
</FormGroup>
</Form>
<Button type="submit" color="primary">Differentiate</Button>
</div>
)
}
}
export default Submit;
Code for LeftWindow.js --
import React, { Component } from 'react';
import Buttons from './Buttons';
import './Custom.css';
import Submit from './Submit';
class LeftWindow extends Component{
constructor(props){
super(props);
}
render(){
return(
<div className="col-3 bg-dark fullheight">
<Buttons/>
<h3 className="center">Enter the function to be differentiated</h3>
<Submit/>
</div>
);
}
}
export default LeftWindow;
This is how your common ancestor file will look like -
Added state having input.
Added a callback "handleInputChange" which updates this input state.
Passed this callback to Both the components.
In your Submit.js file you will need to change your input tag with this
<Input
type="text"
value={this.props.input}
onChange={e => {
this.props.handleInputChange(e.target.value);
}}
/>
Also in your Buttons.js file call this.props.handleInputChange on Button click.
<Button
onClick={() => {
this.props.handleInputChange(button.label)
}}
color={colours[button.type]}
className="buttonsize"
>
{button.label}
</Button>
That's it.
Hope I could help!
In React you work with tree of components where data can travel from top to bottom. It is possible to notify parent component about changed data via passed callbacks. If you have two components that have common ancestor, you can share data between them through this common ancestor.
Let's say you have component Parent which renders your Buttons and Submit components. If you store your data (or state) in Parent and pass this data as props with callbacks, your components then can notify Parent about things happened and parent can change it's state and pass new state as props to children.
There is a "state management" solutions when your data lives detached of your components and injected on one by one basis. In such you won't need parent to store the data, but if we talk about pure react - to share data between branches in react tree, this branches should have common ancestor somewhere in the tree.
I'm trying to submit a Form from Child Component through a button placed in Parent. The Parent is a Slide which has a Next Button and I display my Child Component in one page of a Slide.
What I want to do is: When I press next button from Parent I have to go to the next slide and also to submit the Form Child.
code:
Parent Component:
import Slider from "react-slick";
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
errors: {}
}
next() {
this.slider.slickNext();
}
previous() {
this.slider.slickPrev();
}
render() {
const settings = {
dots: true,
infinite: false
};
return (
<div>
<Slider ref={c => (this.slider = c)} {...settings}>
<div style={{ textAlign: "center" }} key={1} >
<Child /> //this is the Child I want to submit once I press Next Button
<Button id="btn" className="btn btn-primary float-right " type="submit" onClick={this.next}> Next</Button> //this is the next button which sould trigger the submit from Child
</div>
<div key={2} >
<Page2/>
<Button className="btn btn-primary float-right " id="btn" onClick={() => this.finish()>Finish</Button>
<Button id="btn" className="btn btn-primary float-right " onClick={this.previous}> Previous</Button>
</div>
</Slider>
</div>
);
}
}
Child Component:
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
info: '',
infoID: 0,
valid:false,
errors: {}
}
this.handleValidSubmit = this.handleValidSubmit.bind(this);
this.onChange = this.onChange.bind(this);
}
handleValidSubmit() {
if(valid){
localStorage.setItem("infoID",this.state.infoID);
this.sendIDToServerAPI(infoID);
}else {
localStorage.setItem("infoID",this.state.infoID);
this.saveNewInfoAPI(info);
}
}
render() {
return (
<div }>
<Container fluid >
<Row >
<Col >
<AvForm id="avForm" onValidSubmit={this.handleValidSubmit} onInvalidSubmit=}>
<Label >Info</Label>
<AvField onChange={this.onChange} name="email" placeholder=""Email/>
........
.......
</AvForm>
</Col>
</Row>
</Container>
</div>
);
}
}
I tried to minimise the components because they are too large, I hope is clear enough what I try to do. Can anyone please help me with a solution/idea? I tried to Google it but couldn't find anything helpful.
Thanks a lot
What you can do is keep a state(a flag) something like submit which will initially be false.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
submit: false
....rest of your code
}
// function to update the submit state
const setFormSubmit = () => this.setState({submit: true})
}
Now you call setFormSubmit on next button click
Now you can pass this state(submit) to your child component.
and whenever the submit is true you will trigger the method to submit the form
You can do this by using componentDidUpdate in your child component.
componentDidUpdate(prevProps, prevState) {
// if prev submit and current submit prop is unequal and the submit is true
if ((prevProps.submit !== this.props.submit) && this.props.submit) {
// Call method to submit form
}
}
Alright I am trying to submit two different forms as independent components in another page
component where I only have one button to submit the data of both forms.
So I am struggling to have a shared state in the page component and I need to pass the whole state of each form component to my page component on submit.
Can anyone recommend a best practice for my use case ?
render() {
return (
<div as={Row} className="container" style={formStyle}>
<Col>
<Form onSubmit={this.submitData}>
<TripForm />
<PostForm heading="add your first blog entry" />
<Button variant="dark" type="submit">
Summing up
</Button>
</Form>
</Col>
</div>
);
}
define your state in the parent component and pass it down in props
class PageComponent = {
state = { } //define your state here
handleChange = () => {} // define a function that handles changing state
submitData = () => {
// in here you can access this.state and then submit form data with that state
}
render() {
return (
<div as={Row} className="container" style={formStyle}>
<Col>
<Form onSubmit={this.submitData}>
<TripForm handleChange={handleChange} someState={someState} />
<PostForm heading="add your first blog entry" handleChange={handleChange} someState={someState}/>
<Button variant="dark" type="submit">
Summing up
</Button>
</Form>
</Col>
</div>
);
}
}
I've also defined someState which you can pass down as props to the child/form components. once you set state in there with handleChange it will set state in the parent component and you can submitData with that state
Hi i have trouble to send method from parrent to child in react. I did this before and it works ... Why it no works anymore?
I am rendering this:
<div>
<ScheduleSelectModal colupdate={this.updateColumn.bind(this)} subject={this.state.selectedSubject} subjectslist={this.state.mySubjects} show={this.state.modalShow} onHide={this.changeModalState.bind(this)}>
</ScheduleSelectModal>
</div>
This is my method:
updateColumn(newSubject,dayId){
console.log("tu som");
console.log(this.state.schedule);
}
My modal:
ScheduleSelectModal extends Component {
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("modal props:");
console.log(this.props.subject);
}
update(){
console.log("updating...");
this.props.updatecolumn("test","test");
}
createList() {
let items = [];
if (this.props.subjectslist !== null)
this.props.subjectslist.map(subject =>
items.push(<Button key={subject.id} block className={"my-1"} onClick={this.update.bind(this)}>{subject.name} </Button>)
);
return items;
}
render() {
return (
<Modal
{...this.props}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
{this.renderHeader()}
</Modal.Title>
</Modal.Header>
<Modal.Body>
<ButtonGroup vertical className={"w-100"}>
{this.createList()}
</ButtonGroup>
</Modal.Body>
</Modal>
);
}
}
And this is warning i am getting:
index.js:1375 Warning: Invalid value for prop `colupdate` on <div> tag. Either remove it from the element, or pass a string or number value to keep it in the DOM.
Really don't know what the issue is. Thx for help
class ParentComponent extends Component {
passedFunction = () => {}
render() {
<ChildComponent passedFunction={this.passedFunction}/>
}
}
class ChildComponent extends Component {
render() {
<div onClick={this.props.passedFunction}></div>
}
}
You can use arrow function to avoid all the bindings. If you want to bind it, bind it in the constructor like so... in the parent component.
constructor() {
this.passedFunction = this.passedFunction.bind(this)
}
<ChildComponent passedFunction={this.passedFunction}/>
I could see that, in your child component you are using :
update(){
console.log("updating...");
this.props.updatecolumn("test","test");
}
but your props for that function is colupdate i.e. you should be using
update(){
console.log("updating...");
this.porps.colupdate("test","test");
}
Hope this helps!
I have a <Form> ( reduxForm ) Where user submits the values , as soon as the values got submitted It goes to another component showResults and that component Returns the Modal Component , Currently Modal Component is displays on the top of the App component ,
How Can I get the Modal component Popup once user have
submitted(pressed the submit button) the values and then using those
values Modal gets displayed accordingly
Form.jsx
<form onSubmit={handleSubmit}>
{allQuestions}
<div>
<button type="submit" disabled={pristine || submitting}>
Submit
</button> // Once your press this button Modal should PopuP
<button type="button" disabled={pristine || submitting} onClick={reset}>
Clear Values
</button>
</div>
</form>
showResults.jsx
<Form
formData={formData}
onSubmit={e => {
this.onSubmit(e);
}}
/>
Modal.jsx
class ShowModal extends React.Component {
state = {
open: false,
};
onOpenModal = () => {
this.setState({ open: true });
};
onCloseModal = () => {
this.setState({ open: false });
};
render() {
const { open } = this.state;
return(
<Modal open={open} onClose={this.onCloseModal}>
<h4>Total : {this.props.total} Out of 10</h4>
</Modal>
</div>)
Either store the model open state outside of the component and use a property (proper way) or ref (the anti pattern way):
<ShowModal ref={(modal) => this.modal = modal} />
and later:
this.modal.setState(show:true)
but the proper property way would be:
<ShowModal open={this.sate.showModal} />
probably since you are using redux you want to store your modal open/close state in redux state as well