I have this checkbox component!
const CheckBox = props =>{
var [show,setshow] = useState(false);
const option = props.name.replace(/\s/g, '');
return(
<div className="filter-option" onClick={e=>setshow(!show)} data={option}>
<div className={show?"check-bock checked":"check-bock"} >
<i className="fa fa-check"></i>
</div>
<label className="font-20">{props.name}</label>
</div>
)
}
The checked class will show checkmark, but if i want to render multiple checkboxes the problem is all checkboxes are checked at once!
I want only one checked and others unchecked!
The solution is to keep in state which checkbox is checked and store this state in parent from where all checkboxes are rendered
const CheckBox = props =>{
const option = props.name.replace(/\s/g, '');
return(
<div className="filter-option" onClick={e=>props.setshow(prev => props.name == prev? '': props.name)} data={option}>
<div className={props.show?"check-bock checked":"check-bock"} >
<i className="fa fa-check"></i>
</div>
<label className="font-20">{props.name}</label>
</div>
)
}
const Parent = () => {
var [show,setshow] = useState('');
return (
<>
<Checkbox name="first" show={"first" === show} setShow={setShow}/>
<Checkbox name="second" show={"second" === show} setShow={setShow}/>
</>
)
}
Related
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?
I am using React Context to create a multistep form, the form must keep the selection when the user clicks the next or previous step. I am using the below code and it's working fine until I reach the stage to add multiple features using the checkbox, once items are checked, user can go to the previous step to edit and press next to go to the next stage where checked checkboxes must remain checked. I cannot figure out how to push each checkbox value to the features array and remove the item from array when the user uncheck. The important part is to retain the selected despite user go to previous or next step.
Context Provider
import React, { useEffect, useState, createContext } from 'react'
const carSpecs = {
make: '', features: [],model: '',serviceHistory: false, warranty: false, trim: '', bodyType: '', transmission:''
}
export const UsedCarListingContext = createContext({})
export function GlobalUsedCarListingProvider (props) {
const [usedCar, setUsedCar] = useState(carSpecs)
useEffect(() => {}, [usedCar])
return (
<UsedCarListingContext.Provider
value={[usedCar, setUsedCar]}
>
{props.children}
</UsedCarListingContext.Provider>
)
}
Car Details Component
export const UsedCarAdDetails = () => {
const [usedCar, setUsedCar] = useContext(UsedCarListingContext)
const changeHandler = (e) => {
const {name, value} = e.target
setUsedCar({
...usedCar,
[name]: value
})
}
const handleChange = ({target: {name, checked}}) => {
setUsedCar({
...usedCar,
[name]: checked
})
}
return (
<div className="container-1100 bg-white shadow-nav-bar w-full h-52 pt-12">
<NavLink to={'/'} className='landing-nav-logo'>
<img
className='mr-20 mt-4 w-66 ottobay-logo-center'
src={OttobayGray}
alt={'logo'}
/>
</NavLink>
</div>
<div className="container-1050 mg-0auto flex justify-between mt-48">
<div className='container-700 p-20'>
<form>
<div className='ad-listing-input-wrapper mb-16-mobile mb-16 w-full-mobile flex items-center'>
<div className='ad-label-container'>
<label className="listing-input-label font-semibold mr-40"
htmlFor="videoLink">History: </label>
</div>
<div className="ad-input-group-container">
<div className='checkbox-group-container'>
<CheckboxWithImage
onChange={handleChange}
name={'serviceHistory'}
checked={usedCar.serviceHistory}
label={'Service History'}
icon={<GiAutoRepair/>}
checkboxTitleClass={'historyCB'}
/>
<CheckboxWithImage
onChange={handleChange}
name={'warranty'}
checked={usedCar.warranty}
label={'Warranty'}
icon={<AiOutlineFileProtect/>}
checkboxTitleClass={'historyCB'}
/>
</div>
</div>
</div>
<div>
<div className='checkbox-group-wrapper'>
{carFeatures.map(item => (
<div className='feature-item'>
<Checkbox
label={item.name}
onChange={handleFeaturesChange}
checked={usedCar && usedCar.features.some(val => val === item.id)}
value={item.id}
/>
</div>
))}
</div>
</div>
<div className="error-container"></div>
</div>
<div className="car-basic-submission-container">
<div> </div>
<button type='submit' className='search-button bg-button-primary text-white font-semibold rounded-4'> Next Step</button>
</div>
</form>
</div>
)
}
You seem to be calling a non existent function handleFeaturesChange in you feature-item checkbox.
Anyway, something like this should work:
const handleFeaturesChange = ({target: {value, checked}}) => {
setUsedCar({
...usedCar,
features: checked ? [
...usedCar.features,
value, // add the value to previously selected features
] : usedCar.features.filter(val => val !== value) // remove the value
})
}
You could potentially replace the value with name string but then you'd need to update the condition in the checked param of the Checkbox to compare it with the name instead.
I have this modalWindow Component, with a form with a preselected "small" option:
import React from "react";
import pizzaStore from "./stores/PizzaStore";
import { observer } from "mobx-react-lite";
import cartStore from "./stores/CartStore";
import { action } from "mobx";
function ModalWindowComponent({ activeModal, setActiveModal }: any) {
const [price, setPrice] = React.useState(pizzaStore.modalProps.price);
console.log(price);
const handlePriceChange = (opt: string) => {
opt === "small"
? setPrice(pizzaStore.modalProps.price)
: opt === "medium"
? setPrice(pizzaStore.modalProps.price * 1.5)
: setPrice(pizzaStore.modalProps.price * 2);
};
const [selectedOption, setSelectedOption] = React.useState("small");
const setClose = () => {
setSelectedOption("small");
setActiveModal(false);
};
let fixedSize = pizzaStore.size;
let size =
selectedOption === "small"
? fixedSize
: selectedOption === "medium"
? fixedSize * 1.5
: fixedSize * 2;
let obj = {
modalName: pizzaStore.modalProps.name,
modalDesc: pizzaStore.modalProps.description,
modalSize: size,
modalPrice: price,
modalImage: pizzaStore.modalProps.imageUrl,
};
return (
<div
className={activeModal ? "modal active" : "modal"}
onClick={() => {
setActiveModal(false);
setSelectedOption("small");
}}
>
<div
className="modal-content"
onClick={(e) => {
e.stopPropagation();
}}
>
<div className="modal-content-header">
<button onClick={() => setClose()}>Close</button>
</div>
<img
src={pizzaStore.modalProps.imageUrl}
className="modal-content-img"
/>
<p className="modal-content-pizza-name">{pizzaStore.modalProps.name}</p>
<p className="modal-content-pizza-desc">
{pizzaStore.modalProps.description}
</p>
<p className="modal-content-pizza-size">{size}см</p>
<p className="modal-content-pizza-weight">
{pizzaStore.setWeight(selectedOption)}грамм
</p>
<p className="modal-content-pizza-price">{price}Руб.</p>
<form
className="modal-content-sizes-form"
onSubmit={(e: any) => {
cartStore.handleSubmitForm(e, obj);
}}
>
<label>
<input
name="radio-size"
value="small"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(selectedOption);
}}
checked={selectedOption === "small"}
className="modal-content-sizes-form-option"
/>
Маленькая
</label>
<label>
<input
name="radio-size"
value="medium"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(selectedOption);
}}
checked={selectedOption === "medium"}
className="modal-content-sizes-form-option"
/>
Средняя
</label>
<label>
<input
name="radio-size"
value="big"
type="radio"
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
}}
checked={selectedOption === "big"}
className="modal-content-sizes-form-option"
/>
Большая
</label>
<button
onClick={() => {
setClose();
console.log(cartStore.cartItems);
}}
>
Добавить
</button>
</form>
</div>
</div>
);
}
export default observer(ModalWindowComponent);
The selectedOption state should update when a radiobutton is clicked.
however, if I try to log in to the console it gives the wrong values.
For example, when you click the medium valued radio button the console logs "small". The other problem is that the price state doesn't update accordingly with the selected option state. I don't quite understand what is wrong.
That's because state update is batching and asynchronous
You setSelectedOption and handlePriceChange in the same function which cause the issue that you won't get the latest update selectedOption
So you would use the original value like so:
onChange={(e) => {
setSelectedOption(e.target.value);
console.log(selectedOption);
handlePriceChange(e.target.value);
}}
Or having a useEffect waiting for selectedOption to change before calling handlePriceChange:
useEffect(() => {
handlePriceChange(selectedOption);
}, [selectedOption]);
setSelectedOption actually doesn't change the value of selectedOption in your onChange handler. My guess is, it always logs the previous value to the console.
To fix this, store e.target.value in a variable and log that.
please need your help to more understand this point, what I'm trying to do is to edit the task after posting it adding flag if the task needs to be edited or not with 'edit' flag
always give me the following error You provided a value prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultValue. Otherwise, set either onChange or readOnly
could me to solve that error , please
so App.js:
const App=()=> {
const [tasks,settasks]=useState([])
// const [title,setTitle] = useState('')
const AddHandler=(new_task)=>{
const new_arr = [...tasks,new_task]
settasks(new_arr)
}
const onKeyDownHandler=(event,taskIndex)=>{
if(taskIndex){
console.log('test')
}else{
const new_task = {'title':event.target.value,'edit':false}
if(event.keyCode === 13){
AddHandler(new_task)
}
}
}
const deleteHandler=(taskIndex)=>{
const new_tasks = [...tasks]
new_tasks.splice(taskIndex,1)
settasks(new_tasks)
}
const EditHandler=(editIndex) => {
const new_tasks = [...tasks]
new_tasks[editIndex].edit= true
settasks(new_tasks)
}
return (
<div className={classes.App}>
<h3>To do List ...</h3>
<div className={classes.Form}>
<Input inputtype="text" typing={onKeyDownHandler} />
<Button>Add</Button>
</div>
<Tasks
tasks={tasks}
edit={EditHandler}
typing={onKeyDownHandler}
delete={deleteHandler}/>
</div>
);
}
export default App;
in tasks.js:
const Tasks=(props)=>{
return(
<div>
{props.tasks.map((task,index)=>(
<Task triggeredit={task.edit}
key={index}
index={index}
click={()=>props.delete(index)}
edit={()=>props.edit(index)}
title={task.title}
typing={props.typing}
address={index}
/>
))}
{/*<input onKeyDown={(event)=>props.typing(event,1)}/>*/}
</div>
)
}
export default Tasks
in task.js:
const Task=(props)=>{
return (
<div className={classes.Task}>
<Input inputtype="checkbox" inputstyle='CheckBox'/>
{props.triggeredit ?
<Input
index ={props.index}
inputtype="text"
value={props.title}
typing={props.typing}/>:<p>{props.title}</p>}
<Button click={props.edit}><i className="fa fa-edit"></i></Button>
<Button click={props.click} style="Danger"><i className="fa fa-trash" aria-hidden="true"></i></Button>
</div>
)
}
export default Task
in Input.js:
const Input =(props) => {
let InputHtml = null
switch (props.inputtype) {
case('text'):
InputHtml = <input placeholder="Add Your Tasks"
type="text"
value={props.value}
onKeyDown={(event)=>props.typing(event,props.index)}
className={classes.InputElement}
ref={props.refs}
/>
break
case ('checkbox'):
InputHtml = <input type="checkbox" className={classes.InputElement}/>
break
default:
InputHtml = <input placeholder="Add Your Tasks" type="text" className={classes.InputElement}/>
break
}
return (
<div className={[classes.Input,classes[props.inputstyle]].join(' ')}>
{InputHtml}
</div>
)
}
export default Input
i have reactjs form with controlled radio button component , but when i click on one radio button it is selecting another radio button in the form but it is sending correct selected button value , i don't know what is the issue with code , can you please help me out.
here is radio button component :
import React, {Component} from 'react';
const CheckboxOrRadioGroup = (props) => (
<div className="form-group">
<label className="form-label-radio">{props.title}</label>
<div className="checkbox-group">
<ul class="radioul">
{props.options.map(opt => {
return (
<li class="radioli" >
<input
id = {props.name}
name={props.name}
onChange={props.handleChange}
value={opt}
checked={ props.selectedOptions.indexOf(opt) > -1 }
type={props.type} /> <label for={opt} className="form-label-radio1" id={props.handleChange}>{opt}
</label>
</li>
);
})}
</ul>
</div>
</div>
);
export default CheckboxOrRadioGroup;
here is the form element and action :
<CheckboxOrRadioGroup
title={'Enter a amount'}
name={'AMOUNT2'}
type={'radio'}
options={this.state.AMOUNT2Options}
selectedOptions = { this.state.newUser.AMOUNT2}
handleChange={this.handleRadioSelection}
/>
handleRadioSelection(e) {
let value = e.target.value;
let name = e.target.name;
this.setState( prevState => ({ newUser :
{...prevState.newUser, AMOUNT2: value
}
}), () => console.log(this.state.newUser)
)
}