I am creating a menu planner and and started with basic form. I want to be able to enter an entre. If set entre has free of certain diets. I want to click on a checkbox, to switch to true. So I can save the state and render to the screen.
I tried different approaches and googled a lot but can't find anything that really works for me. I can get the checkboxes to switch to true, but it doesn't save the value changes to undefined. Or I had it work once, but it checked all the other checkboxes in the side object. Not what i want.
const ServiceForm = () => {
const service = {
entre:{
value:"",
veg:false,
glut:false,
dairy:false
},
side:{
value:"",
veg:false,
glut:false,
dairy:false
}
}
const [state, dispatch] = useReducer(reducer, service)
console.log(state)
return (
<div>
<form className="form">
<div className="meal-container">
<div className="entre-form">
<input
type="text"
name="entre.value"
onChange={(event) => {
dispatch({ type: "ENTRE", payload: event.target.value });
}}
className="entree"
placeholder="entree"
/>
<div className="diets">
<input
onChange={() => {
dispatch({ type: "ENTRE", veg: !state.entre.veg });
}}
checked={state.entre.veg}
type="checkbox"
/>
<label>Veg</label>
<input type="checkbox" />
<label>Gluten</label>
<input type="checkbox" />
<label>Dairy</label>
</div>
</div>
<div className="entre-form">
<input type="text" name="sideOne" placeholder="Side One" />
<div className="diets">
<input
onChange={() => {
dispatch({ type: "VEG", payload: !state.vegetarian });
}}
checked={state.vegetarian}
type="checkbox"
/>
<label>Veg</label>
<input type="checkbox" />
<label>Gluten</label>
<input type="checkbox" />
<label>Dairy</label>
</div>
</div>
<div className="entre-form">
<input name="entre.value" placeholder="Side Two" />
<div className="diets">
<input type="checkbox" />
<label>Veg</label>
<input type="checkbox" />
<label>Gluten</label>
<input type="checkbox" />
<label>Dairy</label>
</div>
</div>
<div className="entre-form">
<textarea
name="description"
className="description"
placeholder="description"
/>
</div>
</div>
</form>
</div>
);
};
export default ServiceForm;
My reducer function
export const reducer = (state, action) =>{
switch(action.type){
case 'ENTRE':return{
...state,
entre:{
value:action.payload,
veg:action.veg
}
}
}
}
You reducer should be. I will also suggest you have separate actions for text input and checkbox.
switch(action.type){
case 'ENTRE':return{
...state,
entre:{
...entre,
value:action.payload,
veg:action.veg
}
}
}
}
Related
I'm attempting to map an array of objects, obtained from an axios.get request, as a set of React component children. Here is the full error I'm receiving:
Uncaught Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.
My goal here is to create an Admin component where new shows can be created, and existing shows can be edited and deleted. I've confirmed that the axios request's response.data is an array of objects, and I'm not passing the objects themselves as children. I'm passing them as props to <ShowListing /> components, and I know that this component works with those props. Can someone please help me figure out what's going wrong here?
Here is the code for the Admin component. It seems that the error is occurring from lines 107-113:
import { useState, useEffect } from 'react'
import styles from './Admin.module.css'
import axios from 'axios'
import FormData from 'form-data'
import ShowListing from './ShowListing.js'
const Admin = async () => {
const [formValues, setFormValues] = useState({
eventTitle: null,
location: null,
date: null,
time: null,
ticket: null,
desc: null,
image: null
})
const [currShows, setCurrShows] = useState(null)
useEffect(async () => {
axios.get("http://localhost:4000/getShows").then(response => {
console.log(response.data)
setCurrShows(response.data)
})
})
const handleSubmit = async (e) => {
e.preventDefault()
const formData = new FormData()
// FILE READER
const getImageFile = () => {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = function () {
resolve(reader.result)
// console.log(`IMAGE FILE:\n ${imageFile}`) // imageFile IS NOT UNDEFINED HERE, BASE64 STRING
}
reader.readAsDataURL(document.getElementById("image").files[0])
})
}
const imageFile = await getImageFile()
Array.from(document.getElementById("form").elements).forEach(element => {
switch (element.name){
case "image":
formData.append(`${element.name}`, imageFile) // UNDEFINED. WHY?
break
case "submit":
break
default:
formData.append(`${element.name}`, element.value)
}
})
console.log([...formData])
try {
const response = axios.post('http://localhost:4000/uploadShow', formData)
console.log(response)
alert('NEW SHOW SUBMITTED')
document.getElementById("form").reset()
} catch (e) {
alert(e)
console.log(e)
}
}
return (
<div>
<div className={styles.main}>
<div className={styles.titleContainer}>
<h1>KMAC ADMIN</h1>
</div>
<div className={styles.formWindow}>
<div className={styles.newShowHeader}>
<h1>New Show</h1>
<form className={styles.showForm} id="form" method="post" encType="multipart/form-data" onSubmit={e => handleSubmit(e)}>
<label htmlFor="eventTitle">Event Title: </label>
<input className={styles.fieldInput} type="text" name="eventTitle" onChange={e => setFormValues({...formValues, eventTitle: e.target.value})}/>
<br />
<label htmlFor="location">Location: </label>
<input className={styles.fieldInput} type="text" name="location"onChange={e => setFormValues({...formValues, location: e.target.value})} />
<br />
<label htmlFor="date">Date: </label>
<input className={styles.fieldInput} type="date" name="date" onChange={e => setFormValues({...formValues, date: e.target.value})}/>
<br />
<label htmlFor="time">Time: </label>
<input className={styles.fieldInput} type="time" name="time" onChange={e => setFormValues({...formValues, time: e.target.value})}/>
<br />
<label htmlFor="ticket">Ticket: </label>
<input className={styles.fieldInput} type="text" name="ticket" onChange={e => setFormValues({...formValues, ticket: e.target.value})}/>
<br />
<textarea name="desc" placeholder="Event Description" rows="8" onChange={e => setFormValues({...formValues, desc: e.target.value})}/>
<br />
<label htmlFor="image">Select Image (15MB or less): </label>
<input type="file" id="image" name="image" accept="image/jpeg" onChange={e => setFormValues({...formValues, image: e.target.files})}/>
<br />
<button className={styles.submit} name="submit" type="submit">Submit</button>
</form>
</div>
</div>
</div>
<div>
{
currShows.map(show => {
return <ShowListing params={show} />
})
}
</div>
</div>
)
}
export default Admin
Here is the code for the ShowListing component:
import { useState } from 'react'
import axios from 'axios'
import styles from './ShowListing.module.css'
// SHOW OBJECT SHAPE:
// eventTitle: null,
// location: null,
// date: null,
// time: null,
// ticket: null,
// desc: null,
// image: null
const ShowListing = (props) => {
// Toggle deletion warning
const [deleteWarning, setDeleteWarning] = useState(false)
const [editForm, setEditForm] = useState(false)
const [formValues, setFormValues] = useState({
eventTitle: null,
location: null,
date: null,
time: null,
ticket: null,
desc: null,
image: null
})
// covert props.params.date to format "year-month-day", call for date input default value
const dateConvert = () => {
const dateArr = props.params.date.split('-')
const year = dateArr.pop()
dateArr.unshift(year)
return dateArr.join('-')
}
// covert props.param.time to 24-hour format, call for time input default value
const timeConvert = () => {
const timeArr = props.params.time.split(' ')
const time = timeArr[0].split(":")
if (timeArr[1] === 'PM')
time[0] = ((parseInt(time[0])) + 12).toString()
if (parseInt(time[0]) < 10)
time[0] = "0" + time[0]
return time.join(":")
}
const handleDelete = () => {
// TODO: delete request with props.params._id
// Alert deletion and reload page
alert(`SHOW DELETED:\n${props.params.eventTitle}`)
window.location.reload()
}
const handleEditSubmit = (e) => {
e.preventDefault()
// TODO: post request for show update with props.params._id
console.log(formValues)
alert(`SHOW EDITED:\n${formValues.eventTitle}\nFORMERLY:\n${props.params.eventTitle}`)
window.location.reload()
}
return (
<div className={styles.temp}>
<div className={styles.container}>
{
deleteWarning &&
<div className={styles.deleteWarning}>
<div><p>Delete this show listing?</p></div>
<div><button className={`${styles.deleteButton} ${styles.deleteYes}`} onClick={handleDelete}>Yes</button></div>
<div><button className={`${styles.deleteButton} ${styles.deleteNo}`} onClick={() => setDeleteWarning(false)}>No</button></div>
</div>
}
<div className={styles.title}>
<p>{props.params.eventTitle}</p>
</div>
<div className={styles.date}>
<p>{`${props.params.date} -- ${props.params.time}`}</p>
</div>
<div className={styles.edit} onClick={() => setEditForm(true)}>
<img src="images/icons8-edit-30.png" />
</div>
<div className={styles.delete} onClick={() => setDeleteWarning(true)}>
<img src="images/icons8-trash-30.png" />
</div>
<br/>
</div>
{
editForm &&
<div className={styles.formContainer}>
<div className={styles.formFrame}>
<form id="editForm" onSubmit={e => handleEditSubmit(e)}>
<label className={styles.formLabel} htmlFor="eventTitle">Event Title: </label>
<br />
<input className={styles.fieldInput} type="text" name="eventTitle" defaultValue={props.params.eventTitle} onChange={e => setFormValues({...formValues, eventTitle: e.target.value})}/>
<br />
<label className={styles.formLabel} htmlFor="location">Location: </label>
<br />
<input className={styles.fieldInput} type="text" name="location" defaultValue={props.params.location} onChange={e => setFormValues({...formValues, location: e.target.value})} />
<br />
<label className={styles.formLabel} htmlFor="date">Date: </label>
<br />
<input className={styles.fieldInput} type="date" name="date" defaultValue={dateConvert()} onChange={e => setFormValues({...formValues, date: e.target.value})}/>
<br />
<label className={styles.formLabel} htmlFor="time">Time: </label>
<br />
<input className={styles.fieldInput} type="time" name="time" defaultValue={timeConvert()} onChange={e => setFormValues({...formValues, time: e.target.value})}/>
<br />
<label className={styles.formLabel} htmlFor="ticket">Ticket: </label>
<br />
<input className={styles.fieldInput} type="text" name="ticket" defaultValue={props.params.ticket} onChange={e => setFormValues({...formValues, ticket: e.target.value})}/>
<br />
<br />
<textarea className={styles.formDesc} name="desc" placeholder="Event Description" rows="8" defaultValue={props.params.desc} onChange={e => setFormValues({...formValues, desc: e.target.value})}/>
<br />
<label className={styles.formLabel} htmlFor="image">Please update image (15MB or less): </label>
<input style={{color: "red"}} type="file" id="image" name="image" accept="image/jpeg" onChange={e => setFormValues({...formValues, image: e.target.files})}/>
<br />
<br />
<button className={styles.submit} name="submit" type="submit">Submit</button>
<button name="cancel" onClick={() => setEditForm(false)}>Cancel</button>
</form>
</div>
</div>
}
</div>
)
}
export default ShowListing
UPDATE:
Per phasma's suggestion, I removed async from the Admin component and the useEffect. This cleared the original error, but now I'm receiving a new error:
Admin.js:107 Uncaught TypeError: Cannot read properties of null (reading 'map')
Why is currShows null when it's being set in the useEffect?
You're marking your Admin component async, which is not valid for a React component. Remove that modifier and the error should clear up.
edit; To answer your new question, currShows is null until the call is completed, which is likely after the first render. You should write something like
currShows === null ? null : currShows.map(show => { /* ... */
to handle that case explicitly. The null could be a loading spinner or something to indicate something's working in the background, but hopefully that helps clear your error.
I am having an issue where my change function makes my checkboxes go from either true/false to just be 'on'. All the fields in my form use onInputChange and they all work accept for the checkboxes. So if I type in the text fields it updates properly and saves to my database with no issue but if I click a checkbox no matter the value that it started as it will change to 'on' and gives a cannot convert varchar to int error. I am looking for a new function to update the checkboxes properly and keep the onInputChange one for the text fields.
I did create one like below and it seems to change from true to false properly but 100% clears the rest of the data already in the form.
const onCheckboxChange = e => {
setUser({
active = !active });
console.log(user)
};
import React, { useState, useEffect } from "react";
import axios from "axios";
import { useParams, Link } from "react-router-dom";
const UpdateUser = () => {
const { collectorID } = useParams();
const [user, setUser] = useState({});
const {collectorOptionsID, programBucketID,financeCompanyID, active, firstName, middleInitial, lastName, collectorCode, aging1to15, aging31to45, aging31to60, agingOver60, programBucketA, programBucketB, programBucketC, programBucketSU, financeCompany, debtType } = user;
const onInputChange = e => {
setUser({
...user, [e.target.name]: e.target.value });
console.log(user)
};
useEffect(() => {
loadUser();
}, []);// eslint-disable-line react-hooks/exhaustive-deps
const loadUser = async () => {
const result = await axios.get(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`);
setUser(result.data);
console.log(result.data);
};
const onSubmit = async e => {
e.preventDefault();
console.log(active);
await axios.put(process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_DEV_UPDATE}${collectorID}` : `${process.env.REACT_APP_PRO_UPDATE}${collectorID}`, {
// await axios.put(`https://support.pawneeleasing.com/PublishedCollectorAPI/api/Collector/${collectorID}`, {
collectorID: collectorID,
collectorOptionsID: collectorOptionsID,
programBucketID: programBucketID,
financeCompanyID: financeCompanyID,
active: active,
firstName: firstName,
middleInitial: middleInitial,
lastName: lastName,
collectorCode: collectorCode,
debtType: debtType,
aging1to15: aging1to15,
aging31to45: aging31to45,
aging31to60:aging31to60,
agingOver60: agingOver60,
programBucketA: programBucketA,
programBucketB: programBucketB,
programBucketC: programBucketC,
programBucketSU: programBucketSU,
financeCompany: financeCompany,
});
};
return (
<div className="newUser">
<h1>Update Collector</h1>
<form className="newUserForm" >
<div className="newUserItem">
{/*active or inactive User*/}
<label>active</label>
<div className="newUserCheckboxContainer">
<input
type='checkbox'
name="active"
defaultChecked={active}
onClick={e => onInputChange(e)}
/>
</div>
{/*Collector First Name*/}
<label>First Name</label>
<input
type="text"
name="firstName"
placeholder="First Name"
defaultValue={firstName}
onChange={e => onInputChange(e)}
/>
{/*Collector Middle Initial*/}
<label>Middle Initial</label>
<input
type="text"
name="middleInitial"
placeholder="Middle Initial"
defaultValue={middleInitial}
onChange={e => onInputChange(e)}
/>
{/*Collector Last Name*/}
<label>Last Name</label>
<input
type="text"
name="lastName"
placeholder="Last Name"
defaultValue={lastName}
onChange={e => onInputChange(e)}
/>
{/*Collector Code First Initial Middle Initial Last Initial*/}
<label>Collector Code</label>
<input
type="text"
name="collectorCode"
placeholder="Collector Code"
defaultValue={collectorCode}
onChange={e => onInputChange(e)}
/>
{/*Aging Bucket selection section */}
<label>Aging Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>1-15<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging1to15}
defaultChecked={aging1to15}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-45<br/>
<input
type='checkbox'
className='AgingBucketCheckbox'
defaultValue={aging31to45}
defaultChecked={aging31to45}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>31-60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxsm'
defaultValue={aging31to60}
defaultChecked={aging31to60}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>Over 60<br/>
<input
type='checkbox'
className='AgingBucketCheckboxlg'
defaultValue={agingOver60}
defaultChecked={agingOver60}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Progam code selection section*/}
<label>Program Bucket</label>
<div className='newUserCheckboxContainer'>
<label className='newUserCheckboxLabel'>A<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketA}
defaultChecked={programBucketA}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>B<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketB}
defaultChecked={programBucketB}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>C<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketC}
defaultChecked={programBucketC}
onChange={e => onInputChange(e)}
/></label>
<label className='newUserCheckboxLabel'>SU<br/>
<input
type='checkbox'
className='programBucketChecbox'
defaultValue={programBucketSU}
defaultChecked={programBucketSU}
onChange={e => onInputChange(e)}
/></label>
</div>
{/*Finance Company selection section*/}
<label>Finance Company</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="financeCompany"
placeholder="financeCompany"
defaultValue={financeCompany}
onChange={e => onInputChange(e)}
/>
</div>
<label>Debt Type</label>
<div className='newUserCheckboxContainer'>
<input
type="text"
name="debtType"
placeholder="debtType"
defaultValue={debtType}
onChange={e => onInputChange(e)}
/>
</div>
<button type='submit' onClick={(event)=>onSubmit(event)} className="userListAddButton">Update Collector</button>
<Link className="userListGoBackButton" to='/users'>Go Back</Link>
</div>
</form>
</div>
);
}
export default UpdateUser;
You need to copy the rest of user properties by rest operator
const onCheckboxChange = e => {
setUser(prev => ({
...prev,
active: !prev.active
}))
};
Example Form So Far
This is my current code that works, without any checkbox handling started.
import React, { useState } from "react";
import "../admin/SysHealthForm.scss";
export default function SysHealthForm() {
const [input, setInput] = useState({
header: "",
content: "",
eta: "",
});
//When any change is registered, update the Name + Value with target.
//Return previous text and display as name: entered value
function handleChange(e) {
const { name, value } = e.target;
setInput((prevInput) => {
return {
...prevInput,
[name]: value,
};
});
}
//Stop Page Refreshing and Console.log the JSON
function handleClick(e) {
e.preventDefault();
console.log(input);
}
return (
<div className="widgit-syshealth">
<h2>System Health</h2>
<form>
<input
name="header"
placeholder="Header"
autoComplete="off"
onChange={handleChange}
value={input.header}
required
></input>
<textarea
name="content"
placeholder="Message"
autoComplete="off"
onChange={handleChange}
value={input.content}
required
></textarea>
<div className="form-school-check">
<div>
<input type="checkbox" id="syshpcb1" value="Fosseway"></input>
<label htmlFor="syshpcb1">Fosse Way</label>
</div>
<div>
<input type="checkbox" id="syshpcb2" value="Mendip"></input>
<label htmlFor="syshpcb2">Mendip</label>
</div>
<div>
<input type="checkbox" id="syshpcb3" value="Nunney"></input>
<label htmlFor="syshpcb3">Nunney</label>
</div>
<div>
<input type="checkbox" id="syshpcb4" value="Hayesdown"></input>
<label htmlFor="syshpcb4">Hayesdown</label>
</div>
<div>
<input type="checkbox" id="syshpcb5" value="Moorlands"></input>
<label htmlFor="syshpcb5">Moorlands</label>
</div>
<div>
<input type="checkbox" id="syshpcb6" value="Cameley"></input>
<label htmlFor="syshpcb6">Cameley</label>
</div>
<div>
<input type="checkbox" id="syshpcb7" value="St Mary's"></input>
<label htmlFor="syshpcb7">St Mary's</label>
</div>
<div>
<input type="checkbox" id="syshpcb8" value="Other"></input>
<label htmlFor="syshpcb8">Other</label>
</div>
</div>
<input
placeholder="ETA For Fix"
onChange={handleChange}
value={input.eta}
name="eta"
></input>
<button type="Submit" onClick={handleClick}>
Submit
</button>
</form>
</div>
);
}
At The Moment, when you submit the data. It logs the header, content and eta etc correctly
but i want it to essentially create an Array of all the checkboxes that are ticked.
I just don't know where i would even begin..
Will be pushing the data back up to a MongoDB Atlas database once recieved.
Thanks
I am new to react. I have almost 15 input controls on UI. Some are dropdowns, some are textboxes, couple of calender controls and radio buttons. I want to retrive all values before submitting a page. Do I need to define 15 props in state object of component for 15 inputs? is there any way to have it in one object.
Also how to set the values of each control. For example for textbox I know, its like
<input type="text" name="username" className="form-control" id="exampleInput" value={this.props.name} onChange={this.handleChange} placeholder="Enter name"></input>
How to handle same for dropdown,calender and radio buttton. Thanks in advance.
Normally, these wouldn't be props, they'd be state (which is different). You can use objects in state. If you're doing a class-based component (class YourComponent extends React.Component), state is always an object you create in the constructor and update with setState. If you're doing this in a function component, typically you use separate state variables for each thing (const [name, setName] = useState("");), but you can use an object if you prefer. There's more about state in the documentation.
That said, if you only want the values when you take an action, you could make the inputs "uncontrolled."
Here's a three-input example using a class component:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
firstName: "",
lastName: "",
about: ""
};
this.handleChange = this.handleChange.bind(this);
}
handleChange({target: {name, value}}) {
this.setState({[name]: value});
}
render() {
const {firstName, lastName, about} = this.state;
const {handleChange} = this;
return <div>
<div>
<label>
First name:
<br/>
<input type="text" value={firstName} name="firstName" onChange={handleChange} />
</label>
</div>
<div>
<label>
Last name:
<br/>
<input type="text" value={lastName} name="lastName" onChange={handleChange} />
</label>
</div>
<div>
<label>
About you:
<br />
<textarea value={about} name="about" onChange={handleChange} />
</label>
</div>
<div>{firstName} {lastName} {(firstName || lastName) && about ? "-" : ""} {about}</div>
</div>;
}
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Here's one using a functional component with discrete state items (usually best):
const { useState } = React;
const Example = () => {
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [about, setAbout] = useState("");
// There's are lots of ways to do this part, this is just one of them
const handleChange = ({target: {name, value}}) => {
switch (name) {
case "firstName":
setFirstName(value);
break;
case "lastName":
setLastName(value);
break;
case "about":
setAbout(value);
break;
}
};
return <div>
<div>
<label>
First name:
<br/>
<input type="text" value={firstName} name="firstName" onChange={handleChange} />
</label>
</div>
<div>
<label>
Last name:
<br/>
<input type="text" value={lastName} name="lastName" onChange={handleChange} />
</label>
</div>
<div>
<label>
About you:
<br />
<textarea value={about} name="about" onChange={handleChange} />
</label>
</div>
<div>{firstName} {lastName} {(firstName || lastName) && about ? "-" : ""} {about}</div>
</div>;
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Here's one using a functional component with an object in state:
const { useState } = React;
const Example = () => {
const [data, setData] = useState({firstName: "", lastName: "", about: ""});
const handleChange = ({target: {name, value}}) => {
setData(current => ({...current, [name]: value}));
};
const {firstName, lastName, about} = data;
return <div>
<div>
<label>
First name:
<br/>
<input type="text" value={firstName} name="firstName" onChange={handleChange} />
</label>
</div>
<div>
<label>
Last name:
<br/>
<input type="text" value={lastName} name="lastName" onChange={handleChange} />
</label>
</div>
<div>
<label>
About you:
<br />
<textarea value={about} name="about" onChange={handleChange} />
</label>
</div>
<div>{firstName} {lastName} {(firstName || lastName) && about ? "-" : ""} {about}</div>
</div>;
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Here is the sample code, I used in my application.
class CreditCardForm extends React.Component {
constructor() {
super()
this.state = {
name: '',
address: '',
ccNumber: ''
}
}
handleChange(e) {
// If you are using babel, you can use ES 6 dictionary syntax
// let change = { [e.target.name] = e.target.value }
let change = {}
change[e.target.name] = e.target.value
this.setState(change)
}
render() {
return (
<form>
<h2>Enter your credit card details</h2>
<label>
Full Name
<input type="name" onChange={(e)=>this.handleChange(e)} value={this.state.name} />
</label>
<label>
Home address
<input type="address" onChange={(e)=>this.handleChange(e)} value={this.state.address} />
</label>
<label>
Credit card number
<input type="ccNumber" onChange={(e)=>this.handleChange(e)} maxlength="16" value={this.state.ccNumber} />
</label>
<button type="submit">Pay now</button>
</form>
)
}
}
You can set name for input and update state base on event.target.name and event.target.value
constructor() {
super();
this.state = {
text: "",
select: "",
radio: ""
};
}
handeInput = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
console.log(this.state);
return (
<div className="App">
<input
onChange={this.handeInput}
type="input"
name="text"
value={this.state.text}
/>
<select
name="select"
onChange={this.handeInput}
value={this.state.select}
>
<option value="option1">option1</option>
<option value="option2">option2</option>
</select>
<input
type="radio"
name="radio"
value="Option1"
checked={this.state.radio === "Option1"}
onChange={this.handeInput}
/>
Option1
<input
type="radio"
name="radio"
value="Option2"
checked={this.state.radio === "Option2"}
onChange={this.handeInput}
/>
Option2
</div>
);
}
You can check here CodeSandBox Hope it helps
I am trying to set up some functionality on this React component so that a user can add and remove empty radio button options to a page that a user can type text into. The only issue that I am having is that I am relatively new to React and am not 100% how to do this.
import React, { Component } from 'react';
class TextRadio extends Component {
constructor() {
super();
state = {
textValue: ""
}
};
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
this.setState({
[name]: value
});
}
addBox = () => {
}
removeBox = () => {
}
render() {
return(
<div>
<div className="form-check">
<input className="form-check-input" type="radio" id="" name="" value="" />
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
The result that I am expecting to happen is to have it so the component can add and remove radio button options from the page depending on the button that the user presses
i was completed just your addBox and RemoveBox functions, i hope that's help you
import React, { Component } from "react";
class TextRadio extends Component {
constructor() {
super();
this.state = {
radioButtons: []
};
}
handleInputChange = event => {
const value = event.target.value;
const name = event.target.name;
};
addBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length === 0) {
radioButtons.push({
id: 1,
name: "radiobutton",
value: "test"
});
return {
radioButtons: radioButtons
};
} else {
radioButtons.push({
id: radioButtons[radioButtons.length - 1].id + 1,
name: "raiodButton_" + (radioButtons[radioButtons.length - 1].id + 1),
value: radioButtons[radioButtons.length - 1].value
});
return {
radioButtons: radioButtons
};
}
});
};
removeBox = () => {
this.setState(prevstate => {
let radioButtons = prevstate.radioButtons;
if (radioButtons.length !== 0) {
radioButtons.pop(radioButtons[radioButtons.length - 1]);
return {
radioButtons: radioButtons
};
} else {
return { radioButtons: radioButtons };
}
});
};
render() {
return (
<div>
<div className="form-check">
{this.state.radioButtons.map(radiobutton => {
return (
<div>
<input
className="form-check-input"
type="radio"
id={radiobutton.id}
name={radiobutton.name}
value={radiobutton.value}
/>
<label className="form-check-label" for="">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
);
})}
</div>
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button
type="button"
className="btn btn-danger"
onClick={this.removeBox}
>
Remove Option
</button>
</div>
);
}
}
export default TextRadio;
https://codesandbox.io/embed/confident-browser-tmojp
I was playing around with your idea and made some changes in the code, just to show you an example, how you can dynamically create new components and store them in applications state and then render out to user based on their actions.
I created new component just for form UI: option, input field and remove button. If user clicks on the Add Option, new item of the component is added to application state and then render out. Remove button is used to remove Item from state.
class TextRadio extends Component {
state = {
optionInputs: []
};
addBox = () => {
const optionInputsUpdated = [
...this.state.optionInputs,
<OptionInput id={uuid.v4()} remove={this.removeBox} />
];
this.setState({ optionInputs: optionInputsUpdated });
};
removeBox = id => {
const optionInputsUpdated = this.state.optionInputs.filter(
item => item.props.id !== id
);
this.setState({ optionInputs: optionInputsUpdated });
};
render() {
return (
<div>
{this.state.optionInputs.map((optionInput, idx) => {
return (
<div key={idx} test="123">
{optionInput}
</div>
);
})}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
</div>
);
}
}
const OptionInput = props => {
return (
<div className="form-check">
<input
className="form-check-input"
type="radio"
id=""
name="radio"
value=""
/>
<label className="form-check-label" for="">
<input className="form-control" type="text" placeholder="" />
</label>{" "}
<button
type="button"
className="btn btn-danger"
onClick={() => props.remove(props.id)}
>
Remove Option
</button>
</div>
);
};
Hope this gives you better understanding, how to achieve your goal.
If you need additional help, just post a comment under this answer, and I will update demo to help you.
Here is DEMO I created from your code: https://codesandbox.io/s/nice-ganguly-s4wls
first you have to initialize an empty array state
this.state={
radioButtons : [{input:''}]
}
then in your return statement you have to loop through the radioButtons array and show the radio button with input
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
then in your addBox function append an object on every click
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
function to remove a radio button object
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
Final code Looks like this :
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App extends React.Component{
constructor(props){
super(props);
this.state={
radioButtons :[{input:''}]
}
}
addBox = () => {
this.setState({radioButtons:[...this.state.radioButtons, {input:''}]})
}
removeBox = () => {
let radioArray = this.state.radioButtons
radioArray.pop()
this.setState({radioButtons:radioArray})
}
render(){
return(
<div>
{
this.state.radioButtons.map(item => (
<div className="form-check">
<input className="form-check-input" type="radio" id="option" name="option" value="option" />
<label className="form-check-label" for="option">
<input class="form-control" type="text" placeholder="" />
</label>
</div>
))
}
<button type="button" className="btn btn-primary" onClick={this.addBox}>
Add Option
</button>
<button type="button" className="btn btn-danger" onClick={this.removeBox}>
Remove Option
</button>
</div>
)
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
codepen Example