React on button click Add textarea - reactjs

I'm making a simple resume portal and I have a textarea(1), in which user can write his/her work experience. To add more than one work experience I have set a button(Add) which basically will add a new textarea(similar to textarea(1) but without label) just below the textarea(1) and so on. This all should be done when button(Add) is clicked.
If I am able to add my code in a child component that would solve my problem I think.
Here's below what I tried:
Child component ( src -> Routes -> UserForm -> Components -> UserDetails -> index.js )
import React from 'react'
import './style.scss';
import { Row, Col } from 'react-bootstrap'
const UserDetails = () => {
return (
<>
<div className='UserDetails'>
<Row>
<Col lg='6'>
<div className='persnlInfo'>
<h4>
Personal Information
</h4>
<p>Your Name</p>
<input type="text" placeholder="Enter here" />
<p>Your Contact</p>
<input type="text" placeholder="Enter here" />
<p>Your Address</p>
<textarea className='formAddress' rows="5" cols="10" placeholder="Enter here" />
<p id='impLinks'>Important Links</p>
<p>Facebook</p>
<input type="text" placeholder="Enter here" />
<p>Instagram</p>
<input type="text" placeholder="Enter here" />
<p>Linkedin</p>
<input type="text" placeholder="Enter here" />
</div>
</Col>
<Col lg='6'>
<h4>
Professional Information
</h4>
<p>Objective</p>
<textarea className='formObjective' rows="5" cols="10" placeholder="Enter here" />
<p>Work Experience</p>
<textarea className='formObjective' rows="3" cols="10" placeholder="Enter here" />
<div className='addButton'>
<input type='button' id='addWrkExp' value='Add' />
</div>
</Col>
</Row>
</div>
</>
)
}
export default UserDetails;
Parent component ( src -> Routes -> UserForm -> index.js )
import React from 'react'
import Pages from '../../Components/HOC/Page/index'
import UserDetails from '../UserForm/Components/UserDetails/index'
class UserForm extends React.Component{
render() {
return(
<>
<Pages
showHeader
showFooter
>
<UserDetails />
</Pages>
</>
);
}
}
export default UserForm;
Output:
To obtain the said above, I've searched up on this but not getting What and Where to put logic/code exactly. If I'm not wrong this can be done using state , props , onClick() and/or something related.
I hope this is enough from my side to understand the problem. Thanks in advance for your help.

You can use a useState for the number of textareas on your page and then when button(add) gets clicked, that state gets increased by one,
This is your Child component ( src -> Routes -> UserForm -> Components -> UserDetails -> index.js )
import React, { useState } from 'react'
import './style.scss';
import { Row, Col } from 'react-bootstrap'
const UserDetails = () => {
const [ workXPs, setWorkXPs ] = useState(1);
return (
<>
<div className='UserDetails'>
<Row>
<Col lg='6'>
<div className='persnlInfo'>
<h4>
Personal Information
</h4>
<p>Your Name</p>
<input type="text" placeholder="Enter here" />
<p>Your Contact</p>
<input type="text" placeholder="Enter here" />
<p>Your Address</p>
<textarea className='formAddress' rows="5" cols="10" placeholder="Enter here" />
<p id='impLinks'>Important Links</p>
<p>Facebook</p>
<input type="text" placeholder="Enter here" />
<p>Instagram</p>
<input type="text" placeholder="Enter here" />
<p>Linkedin</p>
<input type="text" placeholder="Enter here" />
</div>
</Col>
<Col lg='6'>
<h4>
Professional Information
</h4>
<p>Objective</p>
<textarea className='formObjective' rows="5" cols="10" placeholder="Enter here" />
<p>Work Experience</p>
{[...Array(workXPs).keys()].map((workXP, i) => {
return <textarea key={i} className='formObjective' rows="3" cols="10" placeholder="Enter here" />;
})}
<div className='addButton'>
<input type='button' id='addWrkExp' value='Add' onClick={() => setWorkXPs(prev => (prev + 1))} />
</div>
</Col>
</Row>
</div>
</>
)
}
export default UserDetails;
if you wanna do more advanced and add remove buttons, check this: https://www.educative.io/blog/react-hooks-tutorial-todo-list
hope this helps 🙂

You could just make a state and change the state on the button click.
Then change the style according to the state.

Parent Component
import React from "react";
import Pages from "../../Components/HOC/Page/index";
import UserDetails from "../UserForm/Components/UserDetails/index";
class UserForm extends React.Component {
state = {
textAreas: [{ text: "" }]
};
addTextArea = () => {
let updatedTextArea = [...this.state.textAreas];
updatedTextArea.push({ text: "" });
this.setState({ textAreas: updatedTextArea });
};
render() {
return (
<>
<Pages showHeader showFooter>
<UserDetails textAreas={this.state.textAreas} clicked={this.addTextArea} />
</Pages>
</>
);
}
}
export default UserForm;
i changed <UserDetails textAreas={textAreas} clicked={this.addTextArea} /> to <UserDetails textAreas={this.state.textAreas} clicked={this.addTextArea} />
in the UserForm component
child Component
import React from 'react'
import './style.scss';
import { Row, Col } from 'react-bootstrap'
const UserDetails = (props) => {
const {textAreas, clicked} = props
return (
<>
<div className='UserDetails'>
<Row>
<Col lg='6'>
<div className='persnlInfo'>
<h4>
Personal Information
</h4>
<p>Your Name</p>
<input type="text" placeholder="Enter here" />
<p>Your Contact</p>
<input type="text" placeholder="Enter here" />
<p>Your Address</p>
<textarea className='formAddress' rows="5" cols="10" placeholder="Enter here" />
<p id='impLinks'>Important Links</p>
<p>Facebook</p>
<input type="text" placeholder="Enter here" />
<p>Instagram</p>
<input type="text" placeholder="Enter here" />
<p>Linkedin</p>
<input type="text" placeholder="Enter here" />
</div>
</Col>
<Col lg='6'>
<h4>
Professional Information
</h4>
<p>Objective</p>
<textarea className='formObjective' rows="5"
cols="10" placeholder="Enter here" />
<p>Work Experience</p>
{textAreas.map(item => (
<textarea className='formObjective' value=
{item.value} rows="3" cols="10" placeholder="Enter
here" />
)) }
<div className='addButton' onClick={clicked}>
<input type='button' id='addWrkExp' value='Add' />
</div>
</Col>
</Row>
</div>
</>
)
}
export default UserDetails;

Related

How can I access the value of my inputs in React?

So I have a regular page, I am working with next js. It looks like this:
"use client"
import SubjectInput from "../../../../components/subjectsInput"
export default function Page()
{
let inputs = []
for (let i = 0; i < numberOfInputsGiven; i++) {
inputs.push(<SubjectInput key={i}/>)
}
return(
<>
<form>
<>
{inputs}
</>
<button type="submit">OK</button>
</form>
</>
)
}
I use a component called "SubjectInput", to generate the input fields I need. Now, my question is, how can I access the value of the inputs that are in SubjectInput? Here is what SubjectInput looks like:
export default function SubjectInput(){
return(
<div>
<div>
<label>Name</label>
<input type="text" placeholder="Enter subject's name" required />
</div>
<div>
<label>From</label>
<input type="time" placeholder="Enter subject's starting time" required />
</div>
<div>
<label>To</label>
<input type="time" placeholder="Enter subject's ending time" required />
</div>
</div>
)
}
I would create a component that just holds one label and input, import it into the main page and change the values through props.
Main page
import Input from "./components/Input";
function App() {
return (
<div className="App">
<form>
<Input label="my text" type="text" value="hello" />
<Input label="my number" type="number" value="123" />
<Input type="submit" />
</form>
</div>
);
}
export default App;
Input component
import React from "react";
const Input = (props) => {
return (
<div>
<label>{props.label}</label>
<input type={props.type} value={props.value} />
</div>
);
};
export default Input;

How do I add more number of rows at a time?

I am trying to build a table-like structure in which I want to add rows dynamically. Here is my code:
import { React, useState } from 'react';
function Table() {
const [add, setAdd] = useState(false);
return (
<div>
<div className="headings">
<p>Item Description</p>
<p>Quantity</p>
<p>Rate</p>
<p>Amount</p>
</div>
<div className="rows">
<input placeholder="Item Description" type="text" />
<input placeholder="Quantity" type="number" />
<input placeholder="Price per piece" type="number" />
<input placeholder="Amount" type="number" />
</div>
{
add ?
<div className="rows" >
<input placeholder="Item Description" type="text" />
<input placeholder="Quantity" type="number" />
<input placeholder="Price per piece" type="number" />
<input placeholder="Amount" type="number" />
</div>
:
<div></div>
}
<button className="add" onClick={()=>setAdd(true)}>Add another line item</button>
</div>
)
}
export default Table;
I have tried adding rows using the button but I am able to add only a single row. The state changes to true so can I reset the state or should I use any other method for adding rows?
You need to set a counter in order to track how many rows you have, and preferably pass it on to another component.
This is not a complete example, neither beautiful one, but the essence is there:
import { React, useState } from "react";
function Rows({ numberOfRows }) {
return [...Array(numberOfRows)].map((element, index) => (
<div key={index}>{`Row Number ${index}`}</div>
));
}
function Table() {
const [numberOfRows, setNumberOfRows] = useState(0);
const addRow = () => {
setNumberOfRows((prev) => prev + 1);
};
const deleteRow = () => {
if (numberOfRows > 0) {
setNumberOfRows((prev) => prev - 1);
}
};
return (
<div>
<div className="headings">
<p>Item Description</p>
<p>Quantity</p>
<p>Rate</p>
<p>Amount</p>
</div>
<Rows numberOfRows={numberOfRows} />
<button className="add" onClick={addRow}>
Add row
</button>
<button className="delete" onClick={deleteRow}>
Delete row
</button>
</div>
);
}
export default Table;
You could keep the lines in state:
import { React, useState } from 'react';
function Table() {
const [addedLines, setAddedLines] = useState([<div key={1} className="rows">
<input placeholder="Item Description" type="text" />
<input placeholder="Quantity" type="number" />
<input placeholder="Price per piece" type="number" />
<input placeholder="Amount" type="number" />
</div>]);
const addLine = () => {
const newLine = (<div key={ addedLines.length + 1 } className="rows" >
<input placeholder="Item Description" type="text" />
<input placeholder="Quantity" type="number" />
<input placeholder="Price per piece" type="number" />
<input placeholder="Amount" type="number" />
</div>)
let a = []
addedLines.forEach((x)=>a.push(x))
a.push(newLine)
setAddedLines(a)
}
return (
<div>
<div className="headings">
<p>Item Description</p>
<p>Quantity</p>
<p>Rate</p>
<p>Amount</p>
</div>
{ addedLines}
<button className="add" onClick={addLine}>Add another line item</button>
</div>
)
}
export default Table;
Codepen: https://codepen.io/guilhermebl/pen/vYxvBWE

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

Making inputs required in a React.js form

What is the best (and simplest) way to require inputs (name, email, content etc.) in React.js. I have searched for quite a while but there seems to be so many varying no-so-clear ways to do this. Ideally I want the inputs user_name, user_email and message to be require in order for the form to be sent successfully. Any help would be appreciated.
import React from 'react';
import { MDBContainer, MDBRow, MDBCol, MDBBtn, MDBIcon } from 'mdbreact';
import NavBar from "./NavBar";
import Footer from "./Footer";
import emailjs from 'emailjs-com';
import{ init } from 'emailjs-com';
function Contact() {
function sendEmail(e) {
e.preventDefault();
emailjs.sendForm('hidden', 'hidden', e.target, 'hidden')
.then((result) => {
alert("Great, your request has been sent!");
}, (error) => {
alert("Oops, something went wrong. Please try again")
});
e.target.reset();
}
return (
<div>
<NavBar />
<br />
<br />
<br />
<br />
<br />
<MDBContainer>
<MDBRow className = "formcontainer">
<MDBCol md="6">
<form onSubmit={sendEmail}>
<p className="h4 text-center mb-4">Fill in the information below to contact me!</p>
<br />
<label htmlFor="defaultFormContactNameEx" className="grey-text" > Your name </label>
<input type="text" id="defaultFormContactNameEx" className="form-control" name="user_name" />
<br />
<label htmlFor="defaultFormContactEmailEx" className="grey-text"> Your email </label>
<input type="email" id="defaultFormContactEmailEx" className="form-control" name="user_email"/>
<br />
<label htmlFor="defaultFormContactSubjectEx" className="grey-text"> Subject</label>
<input type="text" id="defaultFormContactSubjectEx" className="form-control" name="user_subject" />
<br />
<label htmlFor="defaultFormContactMessageEx" className="grey-text"> Your message </label>
<textarea type="text" id="defaultFormContactMessageEx" className="form-control" rows="7" name="message"/>
<div className="text-center mt-4">
<MDBBtn color="warning" outline type="submit">
Send
<MDBIcon far icon="paper-plane" className="ml-2" />
</MDBBtn>
</div>
</form>
</MDBCol>
</MDBRow>
</MDBContainer>
<br />
<br />
<Footer />
</div>
);
};
export default Contact;
The easier way to do that is using the required attribute in each of the form's elements.
i.e:
<input required type="email" id="defaultFormContactEmailEx" className="form-control" name="user_email"/>
You can absolutely check if all inputs are not empty when submiting the form too
More info about the required attribute here

Clicking Next button doesn't taking it to next page

import React from 'react';
import { Link } from 'react-router';
import { Field } from 'redux-form';
import BaseForm from '..//BaseForm';
export default class XXXXXXX extends BaseForm {
componentWillMount() {
this.props.retrieveAcademies();
}
render() {
const {
handleSubmit,
formatGraduationDate,
pristine,
submitting,
infoError,
acadamyId
} = this.props;
return (
<div className="col-md-8 col-lg-8 mscs-text">
<div>
<h1 className="mscs-head">Self Registration </h1> <hr />
<form onSubmit={handleSubmit(this.props.startRegistrationProcess.bind(this))} className="registration-step1Class">
<div className="form-section">
<label htmlFor="firstName">First Name:</label>
<Field name="firstName" component={this.renderInputield} label="Enter first Name:" />
</div>
<div className="form-section">
<label htmlFor="lastName">Last Name:</label>
<Field name="lastName" component={this.renderInputield} label="Enter last Name:" />
</div>
<div id="datetimepicker2" className="form-section">
<label htmlFor="dob">Date of Birth:</label>
<Field name="dob" component={this.renderReactDatePicker} type="text" />
</div>
<div className="form-section">
<label htmlFor="maritimeAcadamy">Maritime Academy:</label>
{this.renderAcademiesSelection(acadamyId)}
</div>
<div className="form-section">
<label htmlFor="Yearpassed">Year Passed:</label>
<Field name="passedYear"
component={this.renderReactDatePicker}
type="text"
formatDate={formatGraduationDate}
/>
</div>
<br />
<div className="form-section">
<label htmlFor="CustomerNumber">Customer ID:</label>
<Field name="CustomerNumber" component={this.renderInputield} type="text" label="Enter Customer ID:" />
</div>
<div className="error validation-error">
{infoError && infoError.error} <br />
</div>
<input type="submit" className="btn btn-success" value="Next" />
<input type="reset" className="btn btn-default" value="Cancel" />
</form>
</div>
</div>
);
}
renderAcademiesSelection(acadamyId) {
return (
<div>
<select className="form-control" {...acadamyId}>
<option key={0} value={''}>{'Select your academy'}</option>
{
this.props.academies && this.props.academies.map(function (item, index) {
return (
<option key={item.Id} value={item.Id}>{item.Name}</option>
);
})
}
</select>
</div>
);
}
}
This is the component. Below is the container. The code has some textboxes and dropdowns and calendar controls.
Container :
function mapStateToProps(state, ownProps) {
return {
academies: state.academies.academies,
infoError: state.signUp.error
};
}
const mapDispatchToProps = (dispatch, ownProps) => {
return {
retrieveAcademies: () => {
dispatch(retrieveAcademies());
},
startRegistrationProcess: dispatchGenerateRegistrationToken.bind(this),
nextPage: ownProps.nextPage,
}
}
The above code samples are my react component and container. When I am clicking on the Next button it is not taking me to the next step and it is not showing any type of error in the console. I don't know where exactly the mistake is. I can't debug since the console is not giving any type of error. Any help is appreciated here.

Resources