How to Handle multiple radio button inputs with one onChange function handler - javascript

i have a scenario where based on a number(say numberOfFlags) i want to render numberOfFlags times an radio button group.Each group has two radio buttons approve and reject as per screenshot attached how to get values of all inputs when they change?
An lastly i have to store result of all radio buttons (approve/reject) in an array and send to API

You need to use two parameters on onChange function. One is for current index and another is for Approve/Reject.
Like below code snippet
onchange = handleOnChage(index, isApproveClicked)

You can achive this in many different ways, but I would probably simple create a state with an array of values in the parent component and pass it to each and every item to toggle its own state depending action.
App.js
export function App() {
const [list, setList] = useState([false, false, false]);
const updateItem = (value, index) => {
let copyList = [...list];
copyList[index] = !value;
setList(copyList);
};
console.log(list)
return (
<div className="App">
{list && (
<>
{list.map((value, index) => (
<Item values={[value, index]} updateItem={updateItem} key={index+"_check"} />
))}
</>
)}
</div>
);
}
Item.js
export default function Item({ values, updateItem }) {
return (
<>
<input
onChange={() => updateItem(values[0], values[1])}
type="checkbox"
checked={values[0] ? "checked" : ""}
/>
</>
);
}

Presented below is one possible way to achieve the desired objective.
Code Snippet
const {useState} = React;
const Thingy = ({...props}) => {
// num-of-flags is obtained from props
const { numOfFlags: nf} = props || {};
// if it is null or not above 0, return "invalid" message to parent
if (!(nf && nf > 0)) return "invalid num-of-flags";
// state variable to store approve/reject status
const [statusObj, setStatusObj] = useState({});
// JSX to render the UI & handle events
return (
<div>
{([...Array(nf).keys()].map(grpIdx => (
<div className="grpBox">
Group num {grpIdx+1} <br/>
<input type='radio' name={grpIdx} id={grpIdx} value={'approve'}
onChange={() => setStatusObj({
...statusObj, [grpIdx]: 'approve',
})}
/>
<label for={grpIdx}>Approve</label>{" "}
<input type='radio' name={grpIdx} id={grpIdx} value={'reject'}
onChange={() => setStatusObj({
...statusObj, [grpIdx]: 'reject',
})}
/>
<label for={grpIdx}>Reject</label>
</div>
)))}<br/>
<button
onClick={() => {
// make API call here
// for verification, displaying an alert-message showing the status
const displayMsg = [...Array(nf).keys()].map(
gi => "Group num " + (+gi+1) + " : " + (gi in statusObj ? statusObj[gi] : '__')
).join(', ');
alert(`Approve-Reject status is: ${JSON.stringify(displayMsg)}`);
}}
>
Submit
</button>
</div>
);
};
ReactDOM.render(
<div>
<div className="demoTitle">DEMO</div>
<Thingy numOfFlags={5} />
</div>,
document.getElementById("rd")
);
.demoTitle {
margin-bottom: 5px;
font-size: 20px;
text-decoration: underline;
}
.grpBox {
margin: 5px; padding: 10px;
border: 2px solid purple;
width: max-content;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script>
<div id="rd" />
Explanation
Inline comments added to the snippet above.
PS: If you'd like to add value to stackoverflow community,

Related

onClick function is not called after I have enabled the button in Reactjs

I have a textarea and a button. The button is disabled by default and when the user starts typing, I enable the button to be clicked. But the problem is that, the onClick function is not called while already disabled = false was set.
I've seen this: button onClick doesn't work when disabled=True is initialized (Reactjs)
Seems to be a good idea, but after I setState with the new value, my component is re-rendering, and I don't really want that.
const refText = useRef(null);
const refBtn = useRef(null);
function handleBtnStatus(e) {
let text = e.target.value;
if(text.replace(/\s/g, "").length > 0) {
refBtn.current.disabled = false;
}
else {
refBtn.current.disabled = true;
}
}
function postThis() {
console.log("You posted! Text:", refText.current.value);
// disable again
refBtn.current.disabled = true;
// delete previous text wrote
refText.current.value = "";
}
return (
<>
{isLogged && (
<div className="container">
<div className="content">
<div className="utool-item-text">
<textarea name="textArea" placeholder="Write something.." ref={refText} onChange={(e) => handleBtnStatus(e)}></textarea>
</div>
<div className="utool-item-post">
<button className="ust-btn-post" ref={refBtn} disabled={true} onClick={postThis}>Da Tweet</button>
</div>
</div>
<div className="posts-section">
<div className="list-posts">
{posts.map((p) => {
return (p.hidden === false ? (
<div className="post" key={p.id}>
<div className="post-text">
<span>{p.text}</span>
</div>
</div>
) : (''))
})}
</div>
</div>
</div>
)}
</>
)
Any help?
Use state instead of refs, re-rendering is ok for your case
Simplified example:
import React, { useState } from 'react';
const SimpleExample = () => {
const [textAreaValue, setTextAreaValue] = useState('');
return (
<>
<button disabled={!textAreaValue} onClick={() => console.log('onClick handler')}>
click me
</button>
<textarea value={textAreaValue} onChange={(e) => setTextAreaValue(e.target.value)} />
</>
);
};
And I would recommend checking this Use state or refs in React.js form components?

How to get data in reactjs when we select checkbox without reloading?

I am trying to make an project in which data loads when we click on the checkbox in reactjs , for example we have this :
<h1> Please select an type </h1>
<input type="checkbox" id="Bike" name="Bike" value="Bike" />
<input type="checkbox" id="car" name="car" value="car" />
And then when we select the bike checkbox, our component loads and shows items from JSON file that only contains bikes without reloading or clicking the submit button
Just like how we search on the react-icon website where we type our input and icons load without reloading the whole page or clicking the submit button, thanks :)
whenever there is a change in your checkbox function
e.target.value will give you the selected data
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
}
You can create a custom onChange function :
<input
type="checkbox"
name="check"
checked={this.state.check}
onChange={(e) => {
this.handleChange({
target: {
name: e.target.name,
value: e.target.checked,
},
});
}}
/>
(In this example I've used radio buttons to simplify things.)
When the checked status of a button is changed call an event handler. This updates the radio button status, and then calls the API with the button id. The API sends back the relevant data, and the handler updates the data state with that.
When the state changes (the state array has length > 0) create a table using the data in state.
const { useState } = React;
// Sample data
const data={bike:[{id:1,name:"Bike1",type:"Type1"},{id:2,name:"Bike2",type:"Type2"}],car:[{id:1,name:"Car1",type:"Car1"},{id:2,name:"Car2",type:"Car2"}]};
// Mock API that sends data based on the
// id it's passed after two seconds
function mockApi(id) {
return new Promise(res => {
setTimeout(() => {
res(JSON.stringify(data[id]));
}, 2000);
});
}
function Example() {
// Two states: one to hold the button status, the
// other to hold the data
const [ radio, setRadio ] = useState({});
const [ data, setData ] = useState([]);
// Destructure the id from the button dataset,
// set the button state, and then get the data using
// that id. When the data is returned update the data state
function handleChange(e) {
const { id } = e.target.dataset;
setRadio(id);
mockApi(id)
.then(res => JSON.parse(res))
.then(setData);
}
// Create an array of cell data
function createCells(obj) {
return Object.values(obj).map(value => {
return <td>{value}</td>;
});
}
// Create an array of rows
function createRows(data) {
return data.map(obj => {
return <tr>{createCells(obj)}</tr>;
});
}
// Add some Radio components, and a table
// When the data state is changed, and the state
// contains some data, create a table
return (
<div>
<div>
<Radio
id="car"
name="radio"
checked={radio === 'car'}
handleChange={handleChange}
/>
<Radio
id="bike"
name="radio"
checked={radio === 'bike'}
handleChange={handleChange}
/>
</div>
<div className="output">
{data.length
? (
<table>
<thead>
<td>#</td>
<td>Name</td>
<td>Type</td>
</thead>
<tbody>{createRows(data)}</tbody>
</table>
)
: 'No data'}
</div>
</div>
);
}
// Returns a Radio component
function Radio({ id, checked, handleChange }) {
return (
<label>
<span>{id}</span>
<input
type="radio"
data-id={id}
checked={checked}
onChange={handleChange}
/>
</label>
);
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
[type="radio"]:hover { cursor: pointer; }
.output { margin-top: 1em; }
label span { text-transform: uppercase; }
table { border-collapse: collapse; }
thead { background-color: #343434; color: white; }
td { padding: 0.25em 0.5em; border: 1px solid darkgray; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
this is a simple sample.
const TestCheckBox = () => {
const [carvalue, setCarValue] = useState(false);
const [Bikevalue, setBikeValue] = useState(false);
return (
<>
<h1> Please select an type </h1>
<input
type="checkbox"
onChange={({ target }) => setBikeValue(target.checked)}
id="Bike"
name="Bike"
value="Bike"
/>
<label>{Bikevalue ? "bike Checked" : "bike Unchecked"}</label>
<div>
<input
type="checkbox"
onChange={({ target }) => setCarValue(target.checked)}
id="car"
name="car"
value="car"
/>
<label>{carvalue ? "Car Checked" : "Car Unchecked"}</label>
</div>
</>
);
};
You can use this method
const [value, setValue] = useState({});
const checkHandle=({target})=>{
let temp={...value,
[target.name]:target.checked
};
}

How to implement a check all button for radio buttons, using React Hooks?

Don't get this confused with checking each radio button I have on the page. I want to implement a check all button that sets the value of a nested object state equal to a certain value. I am storing each question in a nested state. Ex.
formQuestions({
kitchen: [question,question2,question3],
living: [question,question2,question3]
})
Four radio buttons are being made for each question. Now one radio button can only be selected at once. Each radio button has its' own value. Ex. `"Good", "Fair", "Poor", "N/A".
When a radio button is selected a state is generated dynamically for that section and question. Ex.
formAnswers({
kitchen: {
question: "Good"
question2: "Poor"
}
})
The goal here is the button that I want to create that checks only one value for each question Ex. clicks button question: "Good", question2: "Good" etc..
For me to set the state of a dynamic value I would need the "Section name" lets call it Name and the "Question" we'll call it question. That would give me access to the value like so formAnswers[Name][question]: value
I am trying to set that state from a component called SectionHeader. These contain the buttons.
SectionHeader.js
import { FormAnswersContext, FormQuestionsContext } from "../../Store";
function SectionHeader({ title, name }) {
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
return (
<div>
<h1 className={styles["Header"]}>{title}</h1>
<div className={styles["MarkAllWrapper"]}>
<button className={styles["MarkAll"]}>
Mark all items as "Good" in this section
</button>
<br />
<button className={styles["MarkAll"]}>
Mark all items as "N/A" in this section
</button>
</div>
</div>
);
}
The parent of Section Header and the rest of the form code excluding the child radio buttons which I have explained, are in another component LivingRoom.js
LivingRoom.js
import { FormQuestionsContext, FormAnswersContext } from "../../Store";
function LivingRoomForm({ Name }) {
const [expanded, setExpanded] = useState(false);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const array = formQuestions.living;
const onChange = (e, name) => {
const { value } = e.target;
setFormAnswers((state) => ({
...state,
[Name]: { ...state[Name], [name]: value },
}));
};
const handleOpen = () => {
setExpanded(!expanded);
};
return (
<div>
<Button
className={styles["CollapseBtn"]}
onClick={handleOpen}
style={{ marginBottom: "1rem", width: "100%" }}
>
<p>LIVING ROOM INSPECTION</p>
<FontAwesome
className="super-crazy-colors"
name="angle-up"
rotate={expanded ? null : 180}
size="lg"
style={{
marginTop: "5px",
textShadow: "0 1px 0 rgba(0, 0, 0, 0.1)",
}}
/>
</Button>
<Collapse className={styles["Collapse"]} isOpen={expanded}>
<Card>
<CardBody>
{array ? (
<div>
<SectionHeader title="Living Room Inspection" name={Name} />
<div
className={styles["LivingRoomFormWrapper"]}
id="living-room-form"
>
{array.map((question, index) => {
const selected =
formAnswers[Name] && formAnswers[Name][question]
? formAnswers[Name][question]
: "";
return (
<div className={styles["CheckboxWrapper"]} key={index}>
<h5>{question}</h5>
<Ratings
section={Name}
question={question}
onChange={onChange}
selected={selected}
/>
</div>
);
})}
</div>
<br />
<ImageUploader name="living" title={"Living Room"} />
</div>
) : (
<div></div>
)}
</CardBody>
</Card>
</Collapse>
</div>
);
}
If there is anything I am missing please let me know, I would be happy to share it. Cheers
Edit: for anyone that needs the radio buttons component.
Ratings.js
import React from "react";
import { FormGroup, CustomInput } from "reactstrap";
function Ratings({ selected, section, question, onChange }) {
return (
<div>
<FormGroup>
<div>
<CustomInput
checked={selected === "Good"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Good`}
value="Good"
label="Good"
/>
<CustomInput
checked={selected === "Fair"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Fair`}
value="Fair"
label="Fair"
/>
<CustomInput
checked={selected === "Poor"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Poor`}
value="Poor"
label="Poor"
/>
<CustomInput
checked={selected === "N/A"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_NA`}
value="N/A"
label="N/A"
/>
</div>
</FormGroup>
</div>
);
}
I do not completely understand your question, I am sorry but I think this will help you.
Here is an implementation of radio buttons using react -
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
render() {
return (
<div className="radio-buttons">
Windows
<input
id="windows"
value="windows"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Mac
<input
id="mac"
value="mac"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Linux
<input
id="linux"
value="linux"
name="platform"
type="radio"
onChange={this.handleChange}
/>
</div>
);
}
}
After a few attempts, I was able to figure out the solution to this issue.
The key here was to figure out a way to get gather each question so that it may be used as a key when setting the state. As my questions were stored in a ContextAPI, I was able to pull them out like so...
this may not be the best solution however it worked for me.
const setStateGood = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "Good" },
}));
});
};
const setStateNA = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "N/A" },
}));
});
};
I was able to map through each question since the name is being passed through props is a key inside the actual object, formQuestions[name]. Because i'm mapping through each one I can set that question as a key and return the new state for each question to whatever I would like.
However, if I was to create an onClick={setState('Good')}, React didn't like that and it created an infinite loop. I will look for more solutions and update this post if I find one.

Handle populating state from multiple checkboxes

I've got a form which builds a list of checkboxes from some data:
<fieldset className="visibility">
<div className="input-container checkbox">
<span className="label">Visible to</span>
<ul>
{
allForces.map(force => {
if (force.name !== 'White' && force.name !== currentMarkerForce) {
return (
<li key={force.uniqid}>
<label>
<input onChange={handleVisibilityChange} name={`visibility-${_.kebabCase(force.name)}`} type="checkbox" value={force.name} checked={markerVisibleTo.includes(force.name) }/>
{force.name} cell
</label>
</li>
)
}
})
}
</ul>
</div>
</fieldset>
As it is usually at least 2 items that will appear and can be checked, I wrote a handler for it which populates an array before posting back to the state, the contents of this array is initially populated from the existing state:
const visibilityChecked = [...markerVisibleTo]
const handleVisibilityChange = ({ target }) => {
const { checked, value } = target
checked ? visibilityChecked.push(value) : visibilityChecked.pop(value)
setMarkerVisibleTo(visibilityChecked)
}
The last line is a call to a useState hook this, mostly works but sometimes I get an odd behaviour where the wrong checkbox is selected:
Can anyone please help shed some light on what is causing this problem?
I may guess that happens because state update is asynchronous and by the time you attempt to apply changes with setMarkerVisibleTo() your state is different from the one you assume it is, you may try to put const visibilityChecked = [...markerVisibleTo] into handleVisibilityChange() body:
const handleVisibilityChange = ({ target }) => {
const visibilityChecked = [...markerVisibleTo]
const { checked, value } = target
checked ? visibilityChecked.push(value) : visibilityChecked.pop(value)
setMarkerVisibleTo(visibilityChecked)
}
Or, as I would write that:
const handleVisibilityChange = ({target:{checked,value}}) => {
const visibilityChecked = checked ?
[...markerVisibleTo, value] :
[...markerVisibleTo].filter(val => val != value)
setMarkerVisibleTo(visibilityChecked)
}
You may find full-blown demo over here:
//dependencies
const { render } = ReactDOM,
{ useState } = React
//mocking source data
const checkItems = [...'abcd']
//check list component
const CheckList = ({items}) => {
const [visibleMarkers, setVisibleMarkers] = useState(checkItems),
onVisibilityChange = ({target:{checked,value}}) => {
const visibilityChecked = checked ?
[...visibleMarkers, value] :
[...visibleMarkers].filter(val => val != value)
setVisibleMarkers(visibilityChecked)
}
return (
<div>
<ul>
{
items.map((item,key) => (
<li {...{key}}>
<label>
Option {item}
<input
type="checkbox"
value={item}
checked={visibleMarkers.includes(item)}
onChange={onVisibilityChange}
/>
</label>
</li>
))
}
</ul>
<span>visibleMarkers: {JSON.stringify(visibleMarkers)}</span>
</div>
)
}
//render
render (
<CheckList items={checkItems} />,
document.getElementById('root')
)
<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.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

Editing entered text on double click using react

I made Todo app using react. On clicking enter the todo gets added in array, then on click on entered todo the todo gets stricked-off. Now i am facing problem to edit the entered todo. I want when i double click the entered todo, it converts into editing mode and then i can edit the todo n save it on enter keypress. My code goes like this:
class App extends React.Component {
constructor(){
super();
this.state={
todo:[]
};
};
entertodo(keypress){
var Todo=this.refs.inputodo.value;
if( keypress.charCode == 13 )
{
this.setState({
todo: this.state.todo.concat({Value:Todo, checked:false, editing:false})
});
this.refs.inputodo.value=null;
};
};
todo(todo,i){
return (
<li className={todo.checked===true? 'line':'newtodo'}>
<div onClick={this.todoCompleted.bind(this, i)}>
<input type="checkbox" className="option-input checkbox" checked={todo.checked} />
<div key={todo.id} className="item">
{todo.Value}
<span className="destroy" onClick={this.remove.bind(this, i)}>X</span>
</div>
</div>
</li>
);
};
remove(i){
this.state.todo.splice(i,1)
this.setState({todo:this.state.todo})
};
todoCompleted(i){
var todo=this.state.todo;
todo[i].checked =todo[i].checked? false:true;
this.setState({
todo:this.state.todo
});
};
allCompleted=()=>{
var todo = this.state.todo;
var _this = this
todo.forEach(function(item) {
item.className = _this.state.finished ? "newtodo" : "line"
item.checked = !_this.state.finished
})
this.setState({todo: todo, finished: !this.state.finished})
};
render() {
return (
<div>
<h1 id='heading'>todos</h1>
<div className="lines"></div>
<div>
<input type="text" ref= "inputodo" onKeyPress={this.entertodo.bind(this)}className="inputodo"placeholder='todos'/>
<span onClick={this.allCompleted}id="all">^</span>
</div>
<div className="mainapp">
<ul className="decor">
{this.state.todo.map(this.todo.bind(this))}
</ul>
</div>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('app'));
.line {
text-decoration: line-through;
color: red;
}
.newtodo{
text-decoration: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="app"></div>
I've been trying something similar, so this gist might help you on the right track. I think you're especially interested the handleClick method. It debounces received click events. Then you can listen for a certain amount of consecutive clicks, like I did on line 33.
However, the transition between view and edit seems to be quite slow (maybe I did something wrong :shrug:) , so if editing should happen frequently, it might be better to fake this behavior with css.*
Style an input type to look like ordinary text, then onFocus, style it like a field.
There is an option for the click events called onDoubleClick={this.handler} at this point in time.
you may use the component
import _ from 'lodash'
import { useState } from 'react'
const inputValue = (e: any): string => e.target.value
function isEnterOrEscapeKeyEvent(event: React.KeyboardEvent<HTMLInputElement>) {
return event.key === 'Enter' || event.key === 'Escape'
}
const EditOnDblClick = () => {
const [isEditing, setisEditing] = useState(false)
const [text, settext] = useState('yoga chitta')
const onEditEnd = () => {
setisEditing(false)
}
return isEditing ? (
<input
value={text}
className="bg-transparent border-2 border-black border-solid"
onKeyDown={(event) => {
if (isEnterOrEscapeKeyEvent(event)) {
event.preventDefault()
event.stopPropagation()
onEditEnd()
}
}}
onChange={_.flow(inputValue, settext)}
onBlur={onEditEnd}
autoFocus
/>
) : (
<div className="select-none" onDoubleClick={() => setisEditing(true)}>
{text}
</div>
)
}
export default EditOnDblClick
Note: Classes are from tailwindcss

Categories

Resources