Render component number of times based on user input - reactjs

I want to render a component the number of times that is based on user input. For example, I have a question that asks how many boxes do you want to create, with a textbox next to it where you can submit a number. Based on whatever that number is, I want to render the box on the screen that amount of times (given that I created a box in another file). How do I do this? Do I do this in my Box.js file (below) or my App.js file where my component is? I need a simple and detailed explanation because I am new to React.
My code using Bootstrap React:
const Box = (props) => {
return (
<div>
<Col xs={12} sm={8} md={8} lg={8} smOffset={2} mdOffset={2} lgOffset={2} >
<Panel bsClass="panel">
<Form horizontal>
<Row>
<Col componentClass={ControlLabel} lg={6}>
How many boxes do you want to create?
</Col>
<Col lg={4}>
<FormControl placeholder="Enter a number..." />
</Col>
<Col lg={2}>
<Button type="submit">
<FaArrowRight style={arrowStyle} />
</Button>
</Col>
</Row>
</Form>
</Panel>
</Col>
</div>
)
};

You'll probably need something else than a functional component here. You'll just need to store the input value in state and render based on that. A very simple example would be:
const Box = React.createClass({
getInitialState: function() {
return {
numItems: null
};
},
render: function() {
let items = [];
for (let i = 0; i < this.state.numItems; i++) {
items.push(<p key={i}>Item {i}</p>);
}
return <div>
<input
type="number"
value={this.state.numItems}
onChange={this.handleValueChange} />
{items}
</div>;
},
handleValueChange: function(e) {
this.setState({
numItems: e.target.value
})
}
});
ReactDOM.render(<Box />, document.getElementById('root'));
<div id="root"></div>
<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>

Add a state to count number of boxes
Update count when click on submit
render box component based on state count (This will automatically happen once you set the state)
Box Component
class BoxComponent extends React.Component {
render() {
return (
<div>BOX</div>
);
}
}
Get user input component
class Box extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
numberOfBoxes:0
};
}
updateBoxes(e) {
e.preventDefault();
this.setState({numberOfBoxes:e.target.value});
}
getBoxes(){
var rows=[];
for(var i=0;i<this.state.numberOfBoxes;i++ ){
rows.push(<BoxComponent/>);
}
return rows;
}
render() {
return (
<div>
<Col xs={12} sm={8} md={8} lg={8} smOffset={2} mdOffset={2} lgOffset={2} >
<Panel bsClass="panel">
<Form horizontal >
<Row>
<Col lg={6}>
How many boxes do you want to create?
</Col>
<Col lg={4}>
<FormControl onChange={this.updateBoxes.bind(this)} placeholder="Enter a number..." />
</Col>
<Col lg={2}>
<Button type="submit">
Submit
</Button>
</Col>
</Row>
</Form>
{this.getBoxes()}
</Panel>
</Col>
</div>
);
}
}
Check following jsfiddle
https://jsfiddle.net/madura/yeu699on/1/
Note that in the sample I added to render boxes when change the count in the input (onchange event). Not to the form submit event. You can add this to form submit event with having reference (check react refs for more information) to your input.

Related

Data from one JSON file is overwriting another JSON dataset when trying to use react-select module in a form in React

Good morning, I have created a new node module by duplicating react-select-country-list found here, https://www.npmjs.com/package/react-select-country-list and modifying the files to display US states instead of countries.
All is well with the new module except for when I go to implement it.
I am trying to build a contact page where users can enter a contact's address with US state(not required) and country.
When I do, the "country" data is overwritten by the "state" data and I do not know the way to differentiate between the countries module and the us states module within my jsx file.
Both country and state dropdowns show US states, the desired behavior is to show countries for the "country" dropdown and US states for the "state" dropdown.
I do not expect there to be any difference between the country dropdown and the state dropdown because I haven't done anything to differentiate between the two because I do not know how.
Will someone please explain to me how I can differentiate from the two modules in my jsx file? Or if there is another/better way to do this please let me know. I am new to React.
Here is my jsx component code:
import React from "react";
import Select from "react-select";
import countryList from "react-select-country-list";
import usstatesList from "react-select-usstates-list";
// reactstrap components
import {
Card,
CardHeader,
CardBody,
CardTitle,
FormGroup,
Form,
Row,
Col
} from "reactstrap";
class AddNewContact extends React.Component {
constructor(props) {
super(props);
this.options = countryList().getData();
this.options = usstatesList().getData();
this.state = {
options: this.options,
value: null
};
}
changeHandler = value => {
this.setState({ value });
};
render() {
return (
<>
<div className="content">
<Row>
<Col md="12">
<Card className="card-user">
<CardHeader>
<CardTitle tag="h5">Add Customer Info</CardTitle>
</CardHeader>
<CardBody>
<Form>
<Row>
<Col className="pl-1" md="6">
<FormGroup>
<label>State</label>
<Select
options={this.state.options}
value={this.state.value}
onChange={this.changeHandler}
/>
</FormGroup>
</Col>
<Col className="pl-1" md="6">
<FormGroup>
<label>Country</label>
<Select
options={this.state.options}
value={this.state.value}
onChange={this.changeHandler}
/>
</FormGroup>
</Col>
</Row>
</Form>
</CardBody>
</Card>
</Col>
</Row>
>
</div>
</>
);
}
}
export default AddNewContact;
You can solve your problem by renaming this.options to this.countryOptions for countries and to this.usStateOptions for US states. In this.state remove options property and use this.countryOptions and this.usStateOptions for your dropdowns. I hope this helps.
Your class should look like this:
class AddNewContact extends React.Component {
constructor(props) {
super(props);
this.countryOptions = countryList().getData();
this.usStateOptions = usstatesList().getData();
this.state = {
value: null
};
}
changeHandler = value => {
this.setState({ value });
};
render() {
return (
<>
<div className="content">
<Row>
<Col md="12">
<Card className="card-user">
<CardHeader>
<CardTitle tag="h5">Add Customer Info</CardTitle>
</CardHeader>
<CardBody>
<Form>
<Row>
<Col className="pl-1" md="6">
<FormGroup>
<label>State</label>
<Select
options={this.usStateOptions}
value={this.state.value}
onChange={this.changeHandler}
/>
</FormGroup>
</Col>
<Col className="pl-1" md="6">
<FormGroup>
<label>Country</label>
<Select
options={this.countryOptions}
value={this.state.value}
onChange={this.changeHandler}
/>
</FormGroup>
</Col>
</Row>
</Form>
</CardBody>
</Card>
</Col>
</Row>
>
</div>
</>
);
}
}

Insert component with variables into another component

I'm attempting to pass a component into another component in react.
The problem I'm getting with different attempts is that some variables in my component are appearing as undefined.
I have a class component like so:
class PleaseUpVote extends Component {
constructor(props) {
super(props)
this.state = { ...INITIAL_STATE };
}
render() {
const { first, last, phone, email, address, address2, city,
zip, headOfHousehold, household, firstHousehold, lastHousehold } = this.state;
return (
<>
<UserHeader />
{/* Page content */}
<Container className="mt--7" fluid>
<Row>
<Col className="order-xl-2 mb-5 mb-xl-0" xl="4">
<Card className="card-profile shadow">
<Row className="justify-content-center">
<Col className="order-lg-2" lg="3">
<div className="card-profile-image">
<a href="#pablo" onClick={e => e.preventDefault()}>
<img
alt="..."
className="rounded-circle"
src={require("assets/img/theme/team-4-800x800.jpg")}
/>
</a>
</div>
</Col>
</Row>
</Container>
</>
)
}
}
In it I have a form that is an input form. The input form should be input if it has not been saved. If it has been saved then it should not be input by default.
My solution was to add the input form as a separate mmethod like so:
const InputForm = () => (
<>
<FormGroup>
<label
className="form-control-label"
htmlFor="input-country"
>
First
</label>
<Input
className="form-control-alternative"
name="firstHousehold"
value={firstHousehold}
onChange={this.onChange}
id="input-firstHousehold"
placeholder="First"
type="text"
/>
</FormGroup>
</>
)
Then just insert this bad boy into PleaseUpVote component like so:
</Col>
<InputForm/>
</Row>
And bam, works like a charm! Right?!... Wrong!
I get that the variables I've placed in input are undefined. How would I be able to achieve this?
The actual idea is, if you want variables or function from parent component to be used in child component, you need to simply pass them from parent component.
<InputForm firstHousehold={firstHousehold} onChange={this.onChange}/>
Of course you need to create onChange function in parent component which handles input change in child component.
Your child component should be,
const InputForm = (props) => (
<>
<FormGroup>
<label
className="form-control-label"
htmlFor="input-country"
>
First
</label>
<Input
className="form-control-alternative"
name="firstHousehold"
value={props.firstHousehold} //access using props
onChange={props.onChange} //access using props
id="input-firstHousehold"
placeholder="First"
type="text"
/>
</FormGroup>
</>
)

changing Redux state does not affect the render view

I am making a generic component and using it in a composite component also i pass some props to the generic component to behave in a certain way.I have used Redux to manage my state but when updating the state in Redux the component didn't re-render with the updated state.
Here are the components:
Generic component
export default class LabeledCheckBox extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
uncheckable: this.props.disableCheckBox
? this.props.disableCheckBox
: false
};
}
handleChange = (event) => {
this.setState({ checked: event.target.checked });
};
render() {
return (
<Form.Group as={Row}>
<Form.Label column="True" sm={9}>
{this.props.controlLabel}
</Form.Label>
<Col sm={3}>
<Form.Check
type="checkbox"
onChange={this.handleChange}
checked={this.state.checked}
onClick={this.props.clicked}
disabled={this.state.uncheckable}
/>
</Col>
</Form.Group>
);
}
}
LabeledCheckBox.propTypes = {
controlLabel: PropTypes.string.isRequired,
disableCheckBox: PropTypes.bool
};
parent component
export default class Endorsments extends Component {
constructor(props, context) {
super(props, context);
this.state = {
open: false
};
}
render() {
const { open } = this.state;
return (
<React.Fragment>
<LabeledCheckBox
clicked={() => this.setState({ open: !open })}
aria-controls="example-collapse-text"
aria-expanded={open}
controlLabel="Apply Endorsment"
disableCheckBox={!this.props.EndorsementSupported}
/>
<Collapse in={this.state.open}>
<div id="example-collapse-text">
<LabeledTextBoxWithCheckBox
controlLabel="BankName"
controlName="setBankName"
style={{ paddingBottom: '10px' }}
/>
<LabeledDateWithCheckBox controlLabel="Cheque Date" />
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="User Name" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="Cheque Sequence" />
</Col>
</Row>
</div>
</Collapse>
</React.Fragment>
);
}
}
Composit Component
class Preferences extends Component {
constructor(props, context) {
super(props, context);
this.props.fetchSupportedVendors();
}
loadScannersBasedOnVendor = () => {
if (
this.props.lastSelected.name === 'scannersVendors' ||
this.props.supportedScannerModule.length !== 0
) {
let select = this.props.supportedScannerModule.filter(
(element) =>
element.vendor ===
this.props.lastSelected.selectedObject.value
);
return select.map((element) => {
return { value: element.Value, label: element.name };
});
}
};
loadScannerMicrFonts = () => {
let supportedMicrs = [];
this.props.scannerCapabilities.supportedMicrFonts.forEach(
(element, key) => {
if (element.supported)
supportedMicrs.push({ value: key, label: element.value });
}
);
return supportedMicrs;
};
loadScannerBitDepth = () => {
let supportedBitDepth = [];
this.props.scannerCapabilities.supportedBitDepth.forEach(
(element, key) => {
if (element.supported)
supportedBitDepth.push({
value: key,
label: element.value
});
}
);
return supportedBitDepth;
};
afterSelectionEnded = () => {
if (
this.props.lastSelected &&
this.props.lastSelected.name === 'scannersModel' &&
!this.props.scannerCapabilities.supportedBitDepth
) {
this.props.loadScannerCapablilitiesToState(
this.props.lastSelected.selectedObject.value
);
}
};
render() {
return (
<Modal
{...this.props}
aria-labelledby="contained-modal-title-vcenter"
centered
dialogClassName="scanningModal"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Preferences
</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row>
<h4>Default Scanner</h4>
</Row>
<Row>
<Col sm={12}>
<LabeledDropDown
controlLabel="Vendor"
controlName="scannersVendors"
placeholder="select a vendor please"
dropdownValues={
this.props.supportedScannerVendor
}
/>
</Col>
</Row>
<Row>
<Col sm={12}>
<LabeledDropDown
controlLabel="Model"
controlName="scannersModel"
placeholder="select model"
dropdownValues={[]}
loadDynamicItems={
this.loadScannersBasedOnVendor
}
afterValueChanged={this.afterSelectionEnded}
/>
</Col>
</Row>
<Row>
<Col sm={6}>
<VerticalLabeledDropDown
controlLabel="MICR Font"
controlName="ScannerMicrFont"
placeholder="select MICR Font"
dropdownValues={[]}
loadDynamicItems={this.loadScannerMicrFonts}
/>
</Col>
<Col sm={6}>
<VerticalLabeledDropDown
controlLabel="Bit Depth"
controlName="scannerBitDipth"
placeholder="select BitDepth"
dropdownValues={[]}
loadDynamicItems={this.loadScannerBitDepth}
/>
</Col>
</Row>
<hr />
<Row>
<h4>Scanner Feature</h4>
</Row>
<Endorsments
EndorsementSupported={
this.props.scannerCapabilities.supportedEndorsement
}
/>
<hr />
<Row>
<h4>General</h4>
</Row>
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="Auto Save" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="View While Scanning" />
</Col>
</Row>
<Row>
<Col sm={6}>
<LabeledCheckBox controlLabel="Use OCR for amount & date" />
</Col>
<Col sm={6}>
<LabeledCheckBox controlLabel="With UV scan" />
</Col>
</Row>
<Row>
<Col sm={12}>
<LabeledCheckBox controlLabel="Dont Show Prining language dialog" />
</Col>
</Row>
</Modal.Body>
<Modal.Footer bsPrefix="internal-modal-footer">
<Button onClick={this.props.onHide}>Close</Button>
<Button onClick={this.props.onHide}>Apply</Button>
</Modal.Footer>
</Modal>
);
}
}
const mapStateToProps = (state) => ({
supportedScannerVendor: state.supportedScanners.supportedVendors,
supportedScannerModule: state.supportedScanners.SupportedScanners,
lastSelected: state.dropdownEvents.dropDownSelectionChanged,
scannerCapabilities: state.supportedScanners.ScannerCapabilities
});
export default connect(mapStateToProps, {
fetchSupportedVendors,
loadScannerCapablilitiesToState
})(Preferences);
Finally
when calling loadScannerCapablilitiesToState the state is changed for this.props.scannerCapabilities.supportedEndorsement but it did not re-render the generic component to be disable or enabled
Am i missing something any help please?
if the Redux state is properly working then you need to change the state in LabeledCheckBox component.
componentDidUpdate() is invoked immediately after updating occurs.
componentDidUpdate = (prevProps, prevState) => {
if (prevProps.disableCheckBox !== this.props.disableCheckBox) {
this.setState({ uncheckable: this.props.disableCheckBox });
}
};
Read more about React lifecycle

How to independently delete dynamically-added input fields in ReactJS

I'm trying to independently delete dynamic inputs in a form in React. I have a user attribute group, and then user attribute children. I need to be able to dynamically add new user attribute groups and children, but then delete those fields without deleting ALL of the child attributes.
Right now, when I delete a child attribute, it deletes one from EACH user attribute group.
I have a working fiddle here that shows my code: https://codesandbox.io/embed/23kr654w80
import React, { Component } from "react";
import { Button, Input, Row, Col, Form, FormGroup, Label } from "reactstrap";
class OfferCriteria extends Component {
constructor(props) {
super(props);
this.state = {
single: "",
attributeSingle: [{ single: "" }],
child: "",
attributeChild: [{ child: " " }]
};
}
handleNameChange = event => {
this.setState({
name: event.target.value
});
};
handleAddSingleAttribute = () => {
this.setState({
attributeSingle: this.state.attributeSingle.concat([{ name: "" }])
});
};
handleRemoveSingleAttribute = idx => () => {
this.setState({
attributeSingle: this.state.attributeSingle.filter(
(s, sidx) => idx !== sidx
)
});
};
handleAddChildAttribute = () => {
this.setState({
attributeChild: this.state.attributeChild.concat([{ child: "" }])
});
};
handleRemoveChildAttribute = idz => () => {
this.setState({
attributeChild: this.state.attributeChild.filter(sidz => idz !== sidz)
});
};
render() {
return (
<div>
<Row>
<Col lg="10">
<hr />
</Col>
<Col lg="2" className="float-right">
<Button color="success" onClick={this.handleAddSingleAttribute}>
Add Attribute Group
</Button>
</Col>
</Row>
{this.state.attributeSingle.map(() => (
<div>
<br />
<Row>
<Col lg="2">
<Label>User Attributes</Label>
</Col>
<Col lg="3" className="float-left">
<FormGroup check inline>
<Input
className="form-check-input"
type="radio"
id="includeUserAttributes"
name="inline-radios"
value="includeUserAttributes"
/>
<Label
className="form-check-label"
check
htmlFor="inline-radio1"
>
Include
</Label>
</FormGroup>
<FormGroup check inline>
<Input
className="form-check-input"
type="radio"
id="excludeUserAttributes"
name="inline-radios"
value="excludeUserAttributes"
/>
<Label
className="form-check-label"
check
htmlFor="inline-radio2"
>
Exclude
</Label>
</FormGroup>
</Col>
<Col lg="4">
<Input
type="text"
name="text-input"
placeholder="This is parent attribute"
/>
</Col>
</Row>
<br />
<Row>
<Col lg="3">
{this.state.attributeChild.map(() => (
<div className="shareholder">
<Input
type="text"
name="text-input"
placeholder="This is child attribute"
/>
</div>
))}
</Col>
<Col lg="3" className="float-right">
{this.state.attributeChild.map(() => (
<div className="shareholder">
<Button
color="primary"
onClick={this.handleAddChildAttribute}
>
Add Attribute Child
</Button>
<br />
</div>
))}
</Col>
<Col lg="3" className="float-right">
{this.state.attributeChild.map(idz => (
<div className="shareholder">
<Button
color="danger"
onClick={this.handleRemoveChildAttribute(idz)}
>
Remove Attribute Child
</Button>
<br />
</div>
))}
</Col>
</Row>
<hr />
</div>
))}
</div>
);
}
}
export default OfferCriteria;
I need these child attributes to delete ONLY in their parent attribute group, instead of deleting all of them from all the attribute groups.
There are a couple of things going wrong with your code, but I'll focus on your initial question.
The problem is that you use the same array of child for all your groups. In order to be correct, you should include the attributeChild state into the attributeSingle objects :
{
attributeSingle: [
{
single: "",
attributeChild: [
{
child: " "
}
]
}
]
}
That way, children remain independent between groups.

React input value is not getting updated although value next to it with same variable is

Basically I use a react input and can't get it's textvalue to update when I click on a button but the value right next to it gets updated on a re-render. I'm also using Redux to call a service to update the values.
So for example if the input quantity loads up with value 55 and I click the '+' button I'd expect the input quantity to get updated to 56 and the value right next to it to be 56. (the value right next to it is there for debug purposes). What actually happens is that the input value stays at 55 and the value right next to it gets updated to 56. Any idea what the problem is?
I have the following code:
class GroceryItemEditModal extends Component {
constructor(props) {
super(props);
this.toggle = this.toggle.bind(this);
this.addItem = this.addItem.bind(this);
this.removeItem = this.removeItem.bind(this);
this.handleQuantityChange = this.handleQuantityChange.bind(this);
this.handleQuantityBlur = this.handleQuantityBlur.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.refreshEditItemData === true) {
this.props.loadEditItem(this.props.selectedEditItem.Id);
}
}
componentDidMount() {
if (this.props.selectedEditItem) {
this.props.loadEditItem(this.props.selectedEditItem.Id);
}
}
addItem() {
const quantity = this.props.selectedEditItem.Quantity + 1;
this.props.setEditItemQuantity(this.props.selectedEditItem.Id, quantity);
}
removeItem() {
const quantity = this.props.selectedEditItem.Quantity - 1;
this.props.setEditItemQuantity(this.props.selectedEditItem.Id, quantity);
}
handleQuantityBlur(event) {
const quantity = event.target.value;
this.props.setEditItemQuantity(this.props.selectedEditItem.Id, quantity);
}
toggle() {
this.props.toggleEditItemVisibility(!this.props.showEditItem);
}
render() {
if (!this.props.selectedEditItem) {
return <div />;
}
let spinner = '';
if (this.props.modifyingEditItem) {
spinner = <FontAwesome name="spinner" spin />;
}
return (
<div>
<Modal isOpen={this.props.showEditItem} toggle={this.toggle} className={this.props.className}>
<ModalHeader toggle={this.toggle}>Edit Grocery Item</ModalHeader>
<ModalBody>
<div>{this.props.selectedEditItem.Name}</div>
<Form>
<FormGroup>
<Label for="blah">Quantity</Label>
<Row>
<Col xs="1">
<Button color="primary" onClick={this.removeItem}>-</Button>
</Col>
<Col xs="9">
<Input name="selectedItemQty" id="selectedItemQty" Value={this.props.selectedEditItem.Quantity} onBlur={this.handleQuantityBlur}></Input>{this.props.selectedEditItem.Quantity}
</Col>
<Col xs="1">
<Button color="primary" onClick={this.addItem}>+</Button>
</Col>
<Col xs="1">
</Col>
</Row>
</FormGroup>
</Form>
{spinner}
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={this.toggle}>Close</Button>{' '}
</ModalFooter>
</Modal>
</div>
);
}
}
is the prop Value in the component Input well spelled ?
<Input
name="selectedItemQty"
id="selectedItemQty"
/*the V is capitalized is that normal ?*/Value={this.props.selectedEditItem.Quantity}
onBlur={this.handleQuantityBlur}>
</Input>
{this.props.selectedEditItem.Quantity}
You can try following thing:
Use an attribute "key" on Input component and give it a random id whenever Input component renders... like this:
< Input key={shordid.generate()} .... whatever props... .>

Resources