Force one selection checkboxe with REACT - reactjs

how can I force only one check among multiple checkboxes ? For now I can select A, B and C but I only want to select ONE choice no more.
Here is my code :
const TabContent = ({ name, typeText }) => {
const [a, setA] = useState(false)
const [b, setB] = useState(false)
const [c, setC] = useState(false)
function handleChange(e) {
e.preventDefault()
}
return (
<form onSubmit={handleChange} >
{a}
{b}
{b}
<input type="checkbox" onChange={(e) => setA(!a)} /> <label> A </label>
<input type="checkbox" onChange={(e) => setB(!b)} /> <label>B</label>
<input type="checkbox" onChange={(e) => setC(!C)} /> <label>C</label>
</form>
{
(a || b) ?
(<div>
<p className="mt-6 py-1">My choices </p>
<SafeAreaView>
<TextInput
onChangeText={(value) => setText(value)}
value={text}
/>
</SafeAreaView>
</div >
)
: ('')
}
{
c?
(...)
}
</div >
);
};

I think you want to use radio input
Example to illustrate this:
https://codesandbox.io/s/elegant-tess-wu7sl7?file=/src/App.js:0-794
import React, { useState } from "react";
export const TabContent = ({ name, typeText }) => {
const [radio, setRadio] = useState(false);
function handleChange(e) {
e.preventDefault();
}
return (
<form onSubmit={handleChange}>
<input
type="radio"
value="A"
onChange={(e) => setRadio(e.target.value)}
checked={radio === "A"}
/>
<label> A </label>
<input
type="radio"
value="B"
checked={radio === "B"}
onChange={(e) => setRadio(e.target.value)}
/>
<label>B</label>
<input
type="radio"
value="C"
checked={radio === "C"}
onChange={(e) => setRadio(e.target.value)}
/>
<label>C</label>
</form>
);
};
export default TabContent;
This way you only keep track of one state.

You can refactor your state with object state instead of using 3 states
const TabContent = ({ name, typeText }) => {
const [check, setCheck] = useState({selected: "", checked: false})
const handleCheckbox = (type) => {
if(type === check.selected && check.checked) {
setCheck({...check, checked: false})
} else {
setCheck({selected: type, checked: true})
}
}
function handleChange(e) {
e.preventDefault()
}
return (
<form onSubmit={handleChange} >
{check.selected}
<input type="checkbox" onChange={(e) => handleCheckbox("a")} /> <label> A </label>
<input type="checkbox" onChange={(e) => handleCheckbox("b")} /> <label>B</label>
<input type="checkbox" onChange={(e) => handleCheckbox("c")} /> <label>C</label>
</form>
{
(check.selected === ("a" || "b")) ?
(<div>
<p className="mt-6 py-1">My choices </p>
<SafeAreaView>
<TextInput
onChangeText={(value) => setText(value)}
value={text}
/>
</SafeAreaView>
</div >
)
: ('')
}
{
(check.selected === "c") ?
(...)
}
</div >
);
};

Related

Edit Data in Parent from Child

Apparently I'm not doing this right, but I'm trying to mutate a map in the parent component from a child component in React 18.2. The parent contains a modal which opens a form. The user then inputs their mutate option (delete/add) and subsequent data from there. I've been going through other questions trying to find an answer for why it's not working as intended, but I can't seem to find much info. Would appreciate any help. This is what I currently have:
Parent.Js
const ParentComponent = ({show, onCloseModalButton}) => {
const resorts = new Map()
resorts.set("Keystone", [39.6069742, -105.97011])
resorts.set("Breckenridge", [39.4808, -106.0676])
resorts.set("Vail", [39.6061, -106.3550])
resorts.set("Crested Butte", [38.8991, -106.9658])
resorts.set("Winter Park", [39.8841, -105.7627])
resorts.set("Copper Mountain", [39.5022, -106.1497])
const [formOption, setFormOption] = React.useState("")
const [formData, setFormData] = React.useState({
resortName: "",
longitude: Number,
latitude: Number,
})
const handleOptionChange = e => {
setFormOption(e.target.value)
}
const handleFormDataChange = e => {
setFormData({
...formData,
[e.target.name]: e.target.value,
})
}
const submitForm = e => {
e.preventDefault()
if (formOption === "Add") {
resorts.set(formData.resortName, [formData.latitude, formData.longitude])
}
if (formOption === "Delete") {
resorts.delete(formData.resortName)
}
}
return (
<div>
<Modal show={show} onCloseModalButton={onCloseModalButton} resorts={resorts} submitForm={submitForm} handleOptionChange={handleOptionChange} handleFormChange={handleFormDataChange} option={formOption} form={formData} />
</div>
)
}
export default ParentComponent;
Modal.js
const Modal = ({show, onCloseModalButton, resorts, submitForm, handleOptionChange, handleFormChange, option, form}) => {
if (!show) {
return null
}
return ReactDOM.createPortal (
<div className='modal-bg'>
<div className='modal'>
<form onSubmit={submitForm}>
<label>Modify:
<select
name="option"
value={option}
onChange={handleOptionChange}
>
<option value="" disabled={true}>-- Choose an Option --</option>
<option value="Add">Add</option>
<option value="Delete">Delete</option>
</select>
</label>
{option === "" ? null :
option === "Add" ?
<div>
<label>Resort Name
<input
type="text"
name="resortName"
value={form.resortName}
onChange={handleFormChange}
/>
</label>
<br></br>
<label>Longitude
<input
type="number"
name="longitude"
value={form.longitude}
onChange={handleFormChange}
/>
</label>
<br></br>
<label>Latitude
<input
type="number"
name="latitude"
value={form.latitude}
onChange={handleFormChange}
/>
</label>
<button type='submit'>Submit</button>
</div> :
<div>
<label>Delete
<select
name="delete"
value={form.resortName}
onChange={handleFormChange}
>
{[...resorts.keys()].map((item)=> {
return <option key={item} value={item}>{item}</option>
})}
</select>
</label>
<button type='submit'>Submit</button>
</div>
}
</form>
<button onClick={onCloseModalButton}>Close Modal</button>
</div>
</div>
, document.body
)
}
export default Modal;

React keep focus on re-rendering input

I am building an e-commerce web app, and have run into a problem making an array of inputs. Inside DescHTML I am trying to handle onChange event, but on each update the focus on the element is lost, making it very annoying to type. Does anyone have any solution or different aproach to this whole problem? Thanks in advance.
import {useState} from 'react'
export default function UploadForm() {
const [name, setName] = useState('')
const [category, setCategory] = useState('')
const [film, setFilm] = useState('')
const [price, setPrice] = useState('')
const [descArr, setDescArr] = useState([''])
function DescHTML() {
return (
<div>
{ descArr.map((item, i) =>
<input type="text" placeholder="TEST" key={i} value={item} onChange={(e) => {
e.preventDefault()
const newArr = [...descArr]
newArr[i] = e.target.value
setDescArr(newArr)
} } /> )}
</div>
)
}
console.log(descArr)
return (
<div>
<form method="POST" action="http://localhost:3500/api/upload" encType='multipart/form-data'>
<input type="file" name="image" />
<input type="text" value={name} name="name" onChange={(e) => setName(e.target.value)} placeholder="Product name" />
<input type="text" value={category} name="category" onChange={(e) => setCategory(e.target.value)} placeholder="Category" />
<input type="text" value={film} name="film" onChange={(e) => setFilm(e.target.value)} placeholder="Film" />
<input type="text" value={price} name="price" onChange={(e) => setPrice(e.target.value)} placeholder="Price" />
<DescHTML />
<hr />
{/*<input type="submit" value="Submit" />*/}
</form>
<button onClick={() => setDescArr([...descArr, ''])}>+</button>
</div>
)
}
Move your descArr and button inside DescHTML check below code you can check more info here.
function DescHTML() {
const [descArr, setDescArr] = useState([""]);
return (
<div>
{descArr.map((item, i) => (
<input
key={i}
type="text"
placeholder="TEST"
value={item}
onChange={(e) => {
e.preventDefault();
const newArr = [...descArr];
newArr[i] = e.target.value;
setDescArr(newArr);
}}
/>
))}
<br />
<button onClick={() => setDescArr([...descArr, ""])}>+</button>
</div>
);
}
First of all - move DescHTML out of UploadForm:
function DescHTML({descArr, setDescArr}) {
return (
<div>
{descArr.map((item, i) =>
<input type="text" placeholder="TEST" key={i} value={item} onChange={(e) => {
e.preventDefault()
const newArr = [...descArr]
newArr[i] = e.target.value
setDescArr(newArr)
}}/>)}
</div>
)
}
and use it in <UploadForm> like this
<DescHTML descArr={descArr} setDescArr={setDescArr}/>
The other problem is the + button which steals the focus on click. Adding onMouseDown handler will fix this.
<button onMouseDown={(e) => e.preventDefault()} onClick={() => setDescArr([...descArr, ''])}>+</button>

Two-way data binding on large forms

(After working through a React.js tutorial, I'm currently coding my first little app to get more practice. So this will be a newbie question and the answer is certainly out there somewhere, but apparently I don't know what to search for.)
Google lists a lot of examples on how to achieve two-way data binding for one input field. But what about large, complex forms, possibly with the option of adding more dynamically?
Let's say my form consists of horizontal lines of input fields. All lines are the same: First name, last name, date of birth and so on. At the bottom of the table, there is a button to insert a new such line. All this data is stored in an array. How do I bind each input field to its respective array element, so that the array gets updated when the user edits a value?
Working example with two lines of two columns each:
import { useState} from 'react';
function App() {
var [name1, setName1] = useState('Alice');
var [score1, setScore1] = useState('100');
var [name2, setName2] = useState('Bob');
var [score2, setScore2] = useState('200');
function changeNameHandler1 (e) {
console.log(e)
setName1(e.target.value)
}
function changeScoreHandler1 (e) {
setScore1(e.target.value)
}
function changeNameHandler2 (e) {
setName2(e.target.value)
}
function changeScoreHandler2 (e) {
setScore2(e.target.value)
}
return (
<div>
<table>
<tbody>
<tr>
<td><input name="name1" id="id1" type="text" value={name1} onChange={changeNameHandler1} /></td>
<td><input name="score1" type="text" value={score1} onChange={changeScoreHandler1} /></td>
</tr>
<tr>
<td><input name="name2" type="text" value={name2} onChange={changeNameHandler2} /></td>
<td><input name="score2" type="text" value={score2} onChange={changeScoreHandler2} /></td>
</tr>
</tbody>
</table>
{name1} has a score of {score1}<br />
{name2} has a score of {score2}<br />
</div>
);
}
export default App;
How do I scale this up without having to add handler functions for hundreds of fields individually?
You can still store your fields in an object and then just add to the object when you want to add a field. Then map through the keys to display them.
Simple example:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState({
field_0: ''
})
const handleChange = (e) => {
setFields({
...fields,
[e.target.name]: e.target.value
})
}
const addField = () => setFields({
...fields,
['field_' + Object.keys(fields).length]: ''
})
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map(key => (
<div>
<input onChange={handleChange} key={key} name={key} value={fields[key]} />
<button onClick={() => removeField(key)}>Remove Field</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
Here is what I think you are trying to achieve in your question:
import { useState } from 'react'
const App = () => {
const [fieldIndex, setFieldIndex] = useState(1)
const [fields, setFields ] = useState({
group_0: {
name: '',
score: ''
}
})
const handleChange = (e, key) => {
setFields({
...fields,
[key]: {
...fields[key],
[e.target.name]: e.target.value
}
})
}
const addField = () => {
setFields({
...fields,
['group_' + fieldIndex]: {
name: '',
score: ''
}
})
setFieldIndex(i => i + 1)
}
const removeField = (key) => {
delete fields[key]
setFields({...fields})
}
return (
<div>
{Object.keys(fields).map((key, index) => (
<div key={key}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, key)} name='name' value={fields[key].name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, key)} name='score' value={fields[key].score} />
<button onClick={() => removeField(key)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
You may want to keep the index for naming in which case you can use an array. Then you would just pass the index to do your input changing. Here is an example of using an array:
import { useState } from 'react'
const App = () => {
const [fields, setFields ] = useState([
{
name: '',
score: ''
}
])
const handleChange = (e, index) => {
fields[index][e.target.name] = e.target.value
setFields([...fields])
}
const addField = () => {
setFields([
...fields,
{
name: '',
score: ''
}
])
}
const removeField = (index) => {
fields.splice(index, 1)
setFields([...fields])
}
return (
<div>
{fields.map((field, index) => (
<div key={index}>
<div>Group: {index}</div>
<label>Name:</label>
<input onChange={(e) => handleChange(e, index)} name='name' value={field.name} />
<label>Score: </label>
<input onChange={(e) => handleChange(e, index)} name='score' value={field.score} />
<button onClick={() => removeField(index)}>Remove Field Group</button>
</div>
))}
<button onClick={() => addField()}>Add Field</button>
<button onClick={() => console.log(fields)}>Log fields</button>
</div>
);
};
export default App;
have multiple solutions to this problem for example:
Solution #1
Using useRef to store value of the field
import { useRef, useCallback } from "react";
export default function App() {
const fullNameInputElement = useRef();
const emailInputElement = useRef();
const passwordInputElement = useRef();
const passwordConfirmationInputElement = useRef();
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
const data = {
fullName: fullNameInputElement.current?.value,
email: emailInputElement.current?.value,
password: passwordInputElement.current?.value,
passwordConfirmation: passwordConfirmationInputElement.current?.value
};
console.log(data);
},
[]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
ref={fullNameInputElement}
id="full_name"
placeholder="Full name"
type="text"
/>
<label htmlFor="email">Email</label>
<input
ref={emailInputElement}
id="email"
placeholder="Email"
type="email"
/>
<label htmlFor="password">Password</label>
<input
ref={passwordInputElement}
id="password"
placeholder="Password"
type="password"
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
ref={passwordConfirmationInputElement}
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
/>
<button type="submit">Submit</button>
</form>
);
}
or still use useState but store all values in one object
import { useState, useCallback } from "react";
const initialUserData = {
fullName: "",
email: "",
password: "",
passwordConfirmation: ""
};
export default function App() {
const [userData, setUserData] = useState(initialUserData);
const updateUserDataHandler = useCallback(
(type) => (event) => {
setUserData({ ...userData, [type]: event.target.value });
},
[userData]
);
const formHandler = useCallback(
() => (event) => {
event.preventDefault();
console.log(userData);
},
[userData]
);
return (
<form onSubmit={formHandler()}>
<label htmlFor="full_name">Full name</label>
<input
id="full_name"
placeholder="Full name"
type="text"
value={userData.fullName}
onChange={updateUserDataHandler("fullName")}
/>
<label>Email</label>
<input
id="email"
placeholder="Email"
type="email"
value={userData.email}
onChange={updateUserDataHandler("email")}
/>
<label htmlFor="password">Password</label>
<input
id="password"
placeholder="Password"
type="password"
value={userData.password}
onChange={updateUserDataHandler("password")}
/>
<label htmlFor="password_confirmation">Password Confirmation</label>
<input
id="password_confirmation"
placeholder="Password Confirmation"
type="password"
value={userData.passwordConfirmation}
onChange={updateUserDataHandler("passwordConfirmation")}
/>
<button type="submit">Submit</button>
</form>
);
}
Solution #2
Or you have multiple libraries that also provide solutions like react-form-hook https://react-hook-form.com/

The input value codes refused to work in react js

What could be wrong with these codes? The input is not working once I add [event.target.name]. If I remove that line of codes, I can see the contents that I type inside the input box. The issue is that I want it to work with this code [event.target.name]. This will enable me pick each inputbox values as entered by the user. There are three input boxes and I need to capture the three values in my useState. Any help on how to write it better?
import React, { useState } from 'react';
import "./addbirthday.css";
import "./home.css";
export default function Addbirthday({setShowAdd}) {
const [inputDatas, setInputData] = useState([
{fullName: '', fullDate: '', relationship: ''}
]);
const handlePublish = () =>{
console.log("Hi ", inputDatas)
}
const handleChangeInput = (index, event) =>{
const values = [...inputDatas];
values[index][event.target.name] = event.target.value
setInputData(values)
}
return (
<div className="container">
<div className= { closeInput? "addContainer" :"addWrapper homeWrapper "}>
<i className="fas fa-window-close" onClick={closeNow} ></i>
{inputDatas.map((inputData, index)=> (
<div key={index} className="addbirth">
<label>Name</label>
<input type="text" name="Fname" placeholder='Namend' value=
{inputData.fullName} onChange = {event => handleChangeInput(index, event)} />
<label>Date</label>
<input type="date" placeholder='Date' name="fdate" value=
{inputData.fullDate} onChange = {event => handleChangeInput(index, event)} />
<label>Relationship</label>
<input type="text" placeholder='Friend' name="frelationship" value=
{inputData.relationship} onChange = {event => handleChangeInput(index, event)}/>
</div>
))}
<button className="addBtn" onClick={handlePublish} >Add</button>
</div>
</div>
)
}
You are not setting the name correctly
Change your input tags name to same as state object name meaning
<input name='fullname' />
I have modified your code a bit. Make it as your own and get it done
Upvote my answer if it helps
https://codesandbox.io/s/jolly-khayyam-51ybe?file=/src/App.js:0-1711
import React, { useState } from "react";
export default function Addbirthday({ setShowAdd }) {
const [inputDatas, setInputData] = useState([
{ Fname: "", fdate: "", frelationship: "" }
]);
const handlePublish = () => {
console.log("Hi ", inputDatas);
};
const handleChangeInput = (index, event) => {
const values = [...inputDatas];
values[index][event.target.name] = event.target.value;
setInputData(values);
console.log(values[index][event.target.name]);
};
return (
<div className="container">
<div className={"addContainer addWrapper homeWrapper"}>
<i className="fas fa-window-close"></i>
{inputDatas.map((inputData, index) => (
<div key={index} className="addbirth">
<label>Name</label>
<input
type="text"
name="Fname"
placeholder="Namend"
value={inputData.fullName}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Date</label>
<input
type="date"
placeholder="Date"
name="fdate"
value={inputData.fullDate}
onChange={(event) => handleChangeInput(index, event)}
/>
<label>Relationship</label>
<input
type="text"
placeholder="Friend"
name="frelationship"
value={inputData.relationship}
onChange={(event) => handleChangeInput(index, event)}
/>
</div>
))}
<button className="addBtn" onClick={handlePublish}>
Add
</button>
</div>
</div>
);
}

How to get each item's value in React.js

const [state, setState] = useState("");
<input onChange={e=>setState(e.target.value)}
I know that I can use onChange={} to get each value but I want to get the
(shirts x pants x shoes) when a button onClick={} event happens
function Somethingie() {
return (
<div>
Shirts: <input className="one" type="number"></input>
Pants: <input className="two" type="number"></input>
Shoes: <input className="three" type="number"></input>
<br /> <br />
<button
onClick={() => {
console.log(
document.querySelector(".one").value *
document.querySelector(".two").value *
document.querySelector(".three").value
);
}}
>
Calculate!
</button>
<br /> <br />
Amount of Possible Outfits: <h1 className="result"></h1>
</div>
);
}
ReactDOM.render(
<Somethingie />,
document.getElementById("root")
);
or do I create an async function?
I would create a state for each input, set the value of the respective input to this state, set the state for each input onChange, and finally, in the onClick of the button just combine the three states.
Example input:
const [state1, setState1] = useState("")
...
const change1 = (e) => {
setState1(e.target.value);
}
...
<input className="one" type="number" value={state1} onChange={change1}/>
onClick of the button
const handleClick = () => {
result = state1 * state2 * state3;
}
function Somethingie() {
return (
<div>
Shirts: <input className="one" type="number"></input>
Pants: <input className="two" type="number"></input>
Shoes: <input className="three" type="number"></input>
<br /> <br />
<button
onClick={() => {
let x = document.getElementsByClassName('result');
x[0].innerHTML =
document.querySelector('.one').value *
document.querySelector('.two').value *
document.querySelector('.three').value;
}}
>
Calculate!
</button>
<br /> <br />
Amount of Possible Outfits: <h1 className="result"></h1>
</div>
);}
ReactDOM.render(
<Somethingie />,
document.getElementById("root")
);
You can directly place calculated value as shown in above code snippet.
You need to keep each of the data in its own useState like this:
import { useState } from "react";
const Combinations = () => {
const [shirts, setShirts] = useState(0);
const [pants, setPants] = useState(0);
const [shoes, setShoes] = useState(0);
const [total, setTotal] = useState(0);
const onChange = (e) => {
console.log(e);
console.log(typeof e.target.value);
console.log(Number(e.target.value));
if (e.target.name === "shirts") setShirts(Number(e.target.value));
if (e.target.name === "pants") setPants(Number(e.target.value));
if (e.target.name === "shoes") setShoes(Number(e.target.value));
};
const calculate = () => {
let all = null;
if (shirts !== 0) {
if (all === null) {
all = shirts;
} else {
all *= shirts;
}
}
if (pants !== 0) {
if (all === null) {
all = pants;
} else {
all *= pants;
}
}
if (shoes !== 0) {
if (all === null) {
all = shoes;
} else {
all *= shoes;
}
}
setTotal(all);
};
return (
<div>
<label htmlFor="shirts">Shirts: </label>
<input
type="number"
id="shirts"
name="shirts"
value={shirts}
onChange={onChange}
/>
<br />
<label htmlFor="pants">Pants: </label>
<input
type="number"
id="pants"
name="pants"
value={pants}
onChange={onChange}
/>
<br />
<label htmlFor="shoes">Shoes: </label>
<input
type="number"
id="shoes"
name="shoes"
value={shoes}
onChange={onChange}
/>
<br />
<button onClick={calculate}>Calculate!</button>
<br />
<p>Amount of Possible Outfits: {total}</p>
</div>
);
}

Resources