Insert component with variables into another component - reactjs

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>
</>
)

Related

React FormControl onChange not being triggered

I am trying to get a simple form working in React but I can't seem to get it working. I am also using react-bootstrap for my GUI components.
I have a component called Inventory that holds the form:
class Inventory extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {
'upc' : ''
}
}
handleChange(event) { this.setState({'upc' : event.target.value }); }
handleSubmit(event) {
// Do some stuff
}
render() {
return (
<Container fluid>
<h1>Inventory Page</h1>
<Row>
<Col>
<Form onSubmit={this.handleSubmit}>
<Form.Group className="mb3" controlId="invUpcForm">
<Form.Label>UPC</Form.Label>
<Form.Control type="text" placeholder="123456789012" />
<Form.Text value={this.state.upc} onChange={this.handleChange} className="text-muted">
Ensure the field is focused, and scan the barcode.
</Form.Text>
</Form.Group>
<Button variant="primary" type="submit">Submit</Button>
</Form>
</Col>
</Row>
</Container>
);
}
}
However, whenever I enter any text into the Input field, handleChange never gets called despite the value of the field changing. If I submit the field, checking the value of upc in state shows it is empty ''.
What am I doing wrong?
According to this example from the react-bootstrap docs, you should be attaching your value and onChange props to the <Form.Control /> component, not <Form.Text />. Form text is for help text, while the form controls handle the actual input elements.
You gotta change like this.
render() {
return (
<Container fluid>
<h1>Inventory Page</h1>
<Row>
<Col>
<Form onSubmit={this.handleSubmit}>
<Form.Group className="mb3" controlId="invUpcForm">
<Form.Label>UPC</Form.Label>
<Form.Control
type="text"
placeholder="123456789012"
value={this.state.upc}
onChange={this.handleChange}
/>
<Form.Text className="text-muted">
Ensure the field is focused, and scan the barcode.
</Form.Text>
</Form.Group>
<Button variant="primary" type="submit">Submit</Button>
</Form>
</Col>
</Row>
</Container>
);
}
It is because Form.Control is a real input component, and Form.Text component is just a simple div/span to render text. You can check it by inspecting element in chrome browser.

Child component renders even though its being memoized

Child component renders even though props are not altered of it.
Following is the parent component
import Child from "./Child";
function Parent({
selected,
show,
setShow,
}) {
const [isRunNow, setIsRunNow] = useState(true);
const [isNotifyMe, setIsNotifyMe] = useState(false);
const handleNotify = useCallback(() => {
setIsNotifyMe(!isNotifyMe);
}, [isNotifyMe]);
const handleSchedule = useCallback(() => {
setIsRunNow(!isRunNow);
}, [isRunNow]);
const WindowForm = () => {
return (
<div>
<Row>
<Col span={12}>
<label className={styles.labelWeight}> Name : </label>
<input
type="text"
value={selected.name}
readOnly={true}
className={styles.input}
/>
<br></br>
<br></br>
<label className={styles.labelWeight}>Description : </label>
<input
type="text"
value={selected.description}
readOnly={true}
className={styles.input}
></input>
<br></br>
<br></br>
<input
type="checkbox"
name="runImm"
id="runImm"
checked={isRunNow}
onChange={handleSchedule}
></input>
<label> Schedule as soon as possible</label>
</Col>
<Col span={12}>
<input
type="checkbox"
name="notifyProcess"
id="notifyProcess"
checked={isNotifyMe}
onChange={handleNotify}
></input>
<label> Notify me when this process ends</label>
<br></br>
<br></br>
<label>Submission Notes : </label>
<textarea
name="notes"
id="notes"
rows="4"
cols="50"
disabled={!isNotifyMe}
></textarea>
</Col>
</Row>
<Row>
<Col span={24}>
<Child isRunNow={isRunNow} />
</Col>
</Row>
</div>
);
};
return (
<Modal
visible={show}
width={800}
centered
maskClosable={false}
onCancel={() => setShow(false)}
title="JKM"
>
<WindowForm />
</Modal>
);
}
Child component is as follows:
import Uploader from "./Uploader";
import Downloader from "./Downloader";
const { TabPane } = Tabs;
const areEqual = (prevProps, nextProps) => {
console.log("passed here"); // THIS IS NEVER LOGGED!!
return true;
};
function Child({ isRunNow }) {
console.log(
`Rendering Childe component...isRunNow value : ${isRunNow}`
);
return (
<div>
<Tabs defaultActiveKey="ka">
<TabPane tab="ka" key="ka">
Panel
</TabPane>
<TabPane tab="sa" key="sa" disabled={isRunNow}>
<Downloader />
</TabPane>
<TabPane tab="da" key="da">
<Uploader />
</TabPane>
</Tabs>
</div>
);
}
export default React.memo(Child, areEqual);
When I check or uncheck check box Notify me, the child component Child re-renders every time. It seems props are not equal and hence its re-rending. I could not figure out where its going wrong.
Please suggest where i m doing wrong.
I suggest to you to separate the WindowForm to a component it's seems that this is the problem.
when I use it as a component the memo start to work
I think is due to this is a function that return component so it's render it no matter what.
the solution:
move WindowForm out side of the component and create a new one with it and then call it and it will work fine
const WindowForm = () => {
return (
<div>
<Row>
<Col span={12}>
<label className={styles.labelWeight}> Name : </label>
<input
type="text"
value={selected.name}
readOnly={true}
className={styles.input}
/>
<br></br>
<br></br>
<label className={styles.labelWeight}>Description : </label>
<input
type="text"
value={selected.description}
readOnly={true}
className={styles.input}
></input>
<br></br>
<br></br>
<input
type="checkbox"
name="runImm"
id="runImm"
checked={isRunNow}
onChange={handleSchedule}
></input>
<label> Schedule as soon as possible</label>
</Col>
<Col span={12}>
<input
type="checkbox"
name="notifyProcess"
id="notifyProcess"
checked={isNotifyMe}
onChange={handleNotify}
></input>
<label> Notify me when this process ends</label>
<br></br>
<br></br>
<label>Submission Notes : </label>
<textarea
name="notes"
id="notes"
rows="4"
cols="50"
disabled={!isNotifyMe}
></textarea>
</Col>
</Row>
<Row>
<Col span={24}>
<Child isRunNow={isRunNow} />
</Col>
</Row>
</div>
);
};
this component and his state to a new component and everything will start to work

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>
</>
);
}
}

Render component number of times based on user input

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.

ReactJS: Triggering event onBlur

I am having child component for input fields which is checking its validation onBlur event.
Child component usage:
<TextInput
id={'lastName'}
label={'Last name'}
required={true}
minChars={3}
maxChars={25}
/>
Child component code:
onBlur(event) {
// logic here
}
render() {
let props = this.props;
return (
<div>
<div className="form-group">
<label htmlFor={props.id}>{props.label}</label>
<input
id={props.id}
type={props.type}
className="form-control"
onBlur={this.onBlur.bind(this)}
/>
<p className="text-danger">{this.error}</p>
</div>
</div>
);
}
This works just fine.
When user submits form from parent component, I would like onBlur to be triggered across all inputs. How could I accomplish this?
In the parent component:
_handleBlur (eventData) {}
render () {
const handleBlur = this._handleBlur.bind(this);
return (
<form>
<ChildComponent onBlur={handleBlur} {...moreProps} />
<ChildComponent onBlur={handleBlur} {...moreProps} />
<ChildComponent onBlur={handleBlur} {...moreProps} />
<button type="submit" onClick={handleBlur}>Submit</button>
</form>
);
}
In the child component:
render () {
const props = this.props;
return (
<div>
<div className="form-group">
<label htmlFor={props.id}>{props.label}</label>
<input
id={props.id}
type={props.type}
className="form-control"
onBlur={props.onBlur}
/>
<p className="text-danger">{this.error}</p>
</div>
</div>
);
}
Based on eventData or other params, you can define which field needs to be blurred. I don't know what exactly needs to happen on blur. In some cases it might be better to split it into a handleBlur and handleBlurAll method.
Hope this helps

Resources