The value of pickedTrue changes from false to true as the component is mounting. but the input defaultChecked doesn't update it just sets as false.
page.js:
function page() {
return (
<Checkbox checked={'1'} />
)
}
export default page
checkbox component:
function Checkbox({checked}) {
const pickedTrue = checked == '1' ? true : false
console.log(pickedTrue)
return (
<input type="checkbox" defaultChecked={pickedTrue}/>
)
}
export default Checkbox
console.log(pickedTrue) output:
undefined
false
undefined
undefined
true
*note: if i use === instead of == it results in false either way.
Use a state variable instead of a constant value. To effectively log changes to your variable you can best use useEffect with a dependency. In your dependency use checked instead of defaultChecked and add an onChange-function so your input will update when you click on the checkbox.
function Checkbox({defaultChecked}) {
const [checked, setChecked] = useState(defaultChecked === '1');
useEffect(() => {
console.log(checked);
}, [checked])
return (
<input type="checkbox"
checked={checked}
onChange={() => setChecked(!checked)}
/>
)
}
You can handle it with useState. Because the state rerender every change.
...
const [checked, setChecked] = useState(false);
console.log(checked);
return (
<input
type="checkbox"
onChange={() => setChecked(!checked)}
defaultChecked={checked}
/>
);
demo
Like everyone else suggested, you might wanna keep track of values in a local state.
Anyway, here's my take on your problem.
It can accept 0 and 1 and sets the checkboxes accordingly.
CodeSandbox - https://codesandbox.io/s/musing-lovelace-41y75?file=/src/Checkbox.jsx
Ref - https://www.robinwieruch.de/react-checkbox/
You are trying to reassign the value of constant which cannot be done. as the value once assigned cannot be changed. And like most of the answers, you should use the state to store the value even when it is coming from the database. You can create a separate function and call it for the defaultChecked. You can do something like this -
function Checkbox({checked}) {
const [checkedValue, setCheckedValue] = useState(checked === 1 ? true : false)
const setChecked = () => {
setChecked(!checked)
}
return (
<input type="checkbox" defaultChecked={checkedValue} onClick={setChecked}/>
)
}
export default Checkbox
Always try using the === strict equality operator which sure the answer isn't anything else but the one with which its type is matched against. When you used the strict equality operator it returned false because you sent a string 1 as a prop and were checking it against the integer 1.
Related
I have several checkboxes running in several checkbox groups. I can't figure out how to uncheck (thus changing the state) on a particular checkbox. FOr some reason I can't reach e.target.checked.
<Checkbox
size="small"
name={item}
value={item}
checked={checkboxvalues.includes(item)}
onChange={(e) => handleOnChange(e)}
/>
and my function
const handleOnChange = (e) => {
const check = e.target.checked;
setChecked(!check);
};
I made a working sample of the component in this sandbox.
You need to create handleOnChange function specific to each group. I have created one for Genre checkbox group in similar way you can create for other groups.
Here is handler function.
const handleOnChangeGenre = (e) => {
let newValArr = [];
if (e.target.checked) {
newValArr = [...state.pillarGenre.split(","), e.target.value];
} else {
newValArr = state.pillarGenre
.split(",")
.filter((theGenre) => theGenre.trim() !== e.target.value);
}
setState({ ...state, pillarGenre: newValArr.join(",") });
};
pass this function as handleOnChange prop to CustomCheckboxGroup as below.
<CustomCheckboxGroup
checkboxdata={genres}
checkboxvalues={state.pillarGenre}
value={state.pillarGenre}
sectionlabel="Genre"
onToggleChange={handleGenreSwitch}
togglechecked={genreswitch}
handleOnChange={handleOnChangeGenre}
/>
comment your handleOnChange function for testing.
check complete working solution here in sandbox -
complete code
Here's how I'd do it: https://codesandbox.io/s/elastic-pateu-flwqvp?file=/components/Selectors.js
I've abstracted the selection logic into a useSelection() custom hook, which means current selection is to be found in store[key].selected, where key can be any of selectors's keys.
items, selected, setSelected and sectionLabel from each useSelection() call are stored into store[key] and spread onto a <CustomCheckboxGroup /> component.
The relevant bit is the handleCheck function inside that component, which sets the new selection based on the previous selection's value: if the current item is contained in the previous selected value, it gets removed. Otherwise, it gets added.
A more verbose explanation (the why)
Looking closer at your code, it appears you're confused about how the checkbox components function in React.
The checked property of the input is controlled by a state boolean. Generic example:
const Checkbox = ({ label }) => {
const [checked, setChecked] = useState(false)
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(!checked)}
/>
<span>{label}</span>
</label>
)
}
On every render, the checked value of the <input /> is set according to current value of checked state. When the input's checked changes (on user interaction) the state doesn't update automatically. But the onChange event is triggered and we use it to update the state to the negative value of the state's previous value.
When dealing with a <CheckboxList /> component, we can't serve a single boolean to control all checkboxes, we need one boolean for each of the checkboxes being rendered. So we create a selected array and set the checked value of each <input /> to the value of selected.includes(item) (which returns a boolean).
For this to work, we need to update the value of selected array in every onChange event. We check if the item is contained in the previous version of selected. If it's there, we filter it out. If not, we add it:
const CheckboxList = ({ items }) => {
const [selected, setSelected] = useState([])
const onChecked = (item) =>
setSelected((prev) =>
prev.includes(item)
? prev.filter((val) => val !== item)
: [...prev, item]
)
return items.map((item) => (
<label key={item}>
<input
type="checkbox"
checked={selected.includes(item)}
onChange={() => onChecked(item)}
/>
<span>{item}</span>
</label>
))
}
Hope that clears things up a bit.
The best way to do it, it's to save selected checkboxes into a state array, so to check or uncheck it you just filter this state array based on checkbox value property that need to be unique.
Try to use array.some() on checkbox property checked. To remove it it's just filter the checkboxes setted up in the state array that are different from that single checkbox value.
I have a component which includes another component (from headlessui/react) defined as follows:
export default function MyComponent(props) {
const [selectedState, setState] = useState('');
return (
<div>
<RadioGroup value={selectedState} onChange={setState}>
...
</RadioGroup>
</div>
)
}
In the onChange I would like to first call a function which does something and then calls setState. However, nomatter what I try, I can't get this to work.
I've tried:
onChange={() => {
doSomethingFirst();
return setState;
}
onChange={() => {
doSomethingFirst();
// This requires an argument and I'm not sure what the argument should be
setState();
}
// Even this fails
onChange={() => setState;}
What do I do to get this working?
When you pass onChange directly to RadioGroup it will invoke your setState with any arguments the RadioGroup supplies. Because setState only takes one argument that's thereby equal to doing onChange={arg => setState(arg)} which already shows how to accomplish what you're trying to do. Just emulate this exact behaviour and add in your function call:
onChange={arg => {
doSomethingHere()
return setState(arg)
}}
This should works.
export default function MyComponent(props) {
const [selectedState, setState] = useState('');
const updateState = (ev)=> {
doSomethingHere();
...
setState()
}
return (
<div>
<RadioGroup value={selectedState} onChange={updateState}>
...
</RadioGroup>
</div>
);
}
ev object passed to updateState function contains <RadioGroup> element. You can inspect it with console.log to see what values it holds.
If you are trying to update the state according to the RadioGroup value, you must be able to read that value inside ev object.
i have two react input components, where in one input component; value changes, i have to set the same value in another input field.
here is my two input components.
<div className="col-md-4">
<label htmlFor="saleamount">Payment </label>
<NumberFormat id="manualPaymentEntry" name="manualPaymentEntry" fixedDecimalScale={true} decimalScale={2} value={manualPayEntry.current} className="form-control" thousandSeparator={true} thousandsGroupStyle="lakh" onChange={(values) => {
const { formattedValue, value } = values;
handleManualPaymentEntry(value);
}} />
</div>
<div className="col-md-4">
<label htmlFor="selectedPayment">Actual Payment</label>
<NumberFormat readOnly={true} fixedDecimalScale={true} decimalScale={2} value={cumulativePay} className="form-control" thousandSeparator={true} thousandsGroupStyle="lakh" />
</div>
</div>
when there is change in manualPaymentEntry i have to set same value to cumulativePay
const [cumulativePay, setCumulativePay] = useState(0);
const manualPayEntry = useRef(0);
useEffect(() => {
setCumulativePay(manualPayEntry.current);
}, [manualPayEntry]);
const handleManualPaymentEntry = (value) => {
let val = parseFloat(value);
manualPayEntry.current = value;
}
i have used useRef for manualPayEntry to set current value to cumulativePay, but when there is change in manualPayEntry it is not setting value to cumulativePay, always i am getting '0' only..., but current value of manualPayEntry shouled get refelcted in cumulativePay
issue got resolved, after chaning onChange to onValueChange, as said in the documentation onchange will no longer get value , instead we need to use onValueChange
react-number-format npm
onValueChange is not same as onChange. It gets called on whenever
there is change in value which can be caused by any event like change
or blur event or by a prop change. It no longer receives event object
as second parameter.
Changing a value manually like you are doing it there:
manualPayEntry.current = value;
Does not trigger the rerendering functionalities of React. Why don't you just add the setCumulativePay directly in the handleManualPaymentEntry?
const handleManualPaymentEntry = (value) => {
let val = parseFloat(value);
manualPayEntry.current = value;
setCumulativePay(value);
}
Or you should treat manualPayEntry as a state as well and not a ref, though it's not really advised to change a state in a useEffect based on another state. I really think changing directly the needed state in your handler function is the best solution.
I am trying to build a select field which will "select" an Entry automatically when there is only one entry in the list. My Problem is, that there will no onChange event triggered, when setting the value in the list. Is there any possibility to send these event programatically?
This is my code so far
export const SelectField = function(props) {
const classes = useStyles();
const {t} = useTranslation();
const [selectedValue, setSelectedValue] = React.useState(undefined);
if (selectedValue === undefined && props.menuEntries.length === 1) {
setSelectedValue(props.menuEntries[0]);
//need to fire an event in this case
} else if (props.addSelectEntry) {
props.menuEntries.push({"name":t("select"), "value":""});
}
return (
<Select
value={selectedValue}
onChange={props.onChange()}
name={props.name}
displayEmpty
className={classes.selectEmpty}
>
{props.menuEntries.map(entry => (
<MenuItem key={entry.name} value={entry.value}>
{entry.name}
</MenuItem>
))}
</Select>);
};
There is an important difference in passing a function as a prop and calling a function.
onChange={props.onChange()}
Will call that function every render.
onChange={props.onChange}
Will pass that function to be called by the component.
For one is the mentioned onChange() to onChange, but that is not all.
It seems the issue is connected to code outside of the provided component. One indicator of an issue is the following:
....
} else if (props.addSelectEntry) {
props.menuEntries.push({"name":t("select"), "value":""}); // This line could be faulty
}
....
I don't know what kind of value menuEntries is, but it should be a state somewhere higher in your component tree. You should also pass in the setter. Probably called something like setMenuEntries. Then call that setter in instead of mutate the prop.
setMenuEntries([...menuEntries, {"name":t("select"), "value":""}])
Only when setting a state a rerender is triggered.
In general, updating props of any function is considered a bad-practise when following the principle of immutability. Eslint can help you with signifying patterns like that.
From the looks of your code it seems to be alright, there is however one thing that might cause your problem.
return (
<Select
value={selectedValue}
onChange={props.onChange} <---
name={props.name}
displayEmpty
className={classes.selectEmpty}
>
{props.menuEntries.map(entry => (
<MenuItem key={entry.name} value={entry.value}>
{entry.name}
</MenuItem>
))}
</Select>);
I changed how you set the onchange function. You did it while also calling the actual function. This will make the function fire when the component renders and not on the change of your controlled value for this select.
I have solved my problem by calling he handler by my own, if i set the initial value:
export const SelectField = function(props) {
const classes = useStyles();
const { t } = useTranslation();
const [selectedValue, setSelectedValue] = React.useState(props.value);
// on startup load test cases
React.useEffect(() => {
if (selectedValue === props.value && props.menuEntries.length === 1) {
setSelectedValue(props.menuEntries[0].value);
props.onChange(props.menuEntries[0].value);
}
}, [selectedValue]);
const updateValue = function(e) {
props.onChange(e.target.value);
setSelectedValue(e.target.value);
};
return (
<Select
value={selectedValue}
onChange={updateValue}
name={props.name}
displayEmpty
className={classes.selectEmpty}
>
{
(props.menuEntries.length !== 1 && props.addSelectEntry) && (
<MenuItem key={t("select")} value={t("select")}>
{t("select")}
</MenuItem>
)
}
{props.menuEntries.map(entry => (
<MenuItem key={entry.name} value={entry.value}>
{entry.name}
</MenuItem>
))}
</Select>);
};
When I am updating the with setState hook, value is not getting updated.
As per my knowledge this setState call is async. How to solve this issue?
export default function ResultItem({result:{ name, result_type, entry, unit, min_val, max_val, in_spec}, handleEntryChange}){
const [inSpec, setInSpec] = useState(in_spec)
const [result, setResult] = useState(entry);
console.log(handleEntryChange)
return(
<Fragment>
<td>{name}</td>
<td>{result_type}</td>
<td>{}</td>
<td>
<input
name={name}
type={result_type === 'numeric' ? "number" : "text"}
value={result || ''}
onChange={(e) => {
let val = parseInt(e.target.value)
setResult(val)
if(val >= min_val && val <= max_val){
setInSpec(true);
}else{
setInSpec(false);
}
console.log(result, inSpec, val)
handleEntryChange({[name]: val, [name]: inSpec});
}}
/>
</td>
<td>{unit}</td>
<td>{inSpec? 'YES': 'NO'}{console.log(in_spec)}</td>
</Fragment>
)
}
You are basically trying to make a custom Input. In general, you need to be careful of how this input is controlled ? On your interface, you have entry (which might be the initial value) but internally you have result.
Both are actually the value in Input context, but they normally are either controlled from external (on the interface) or tracked internally, but not both.
Just imaging if entry is changed, what could be result ?
The right way is the following
const Input = ({ value, onChange }) => {
// no internal state here
return (
<input value={value} onChange={onChange} />
)
}
This is a pure pass through model with both value and onChange driven by external.
Now you might ask how do I change the look and feel of my custom Input.
You can now write wrapper for onChange to inject your own implementation.
const onValueChange = e => {
// e.target.value
// do your own handling and then
// if need call onChange(e) to handle external
}
<input onChange={onValueChange} />
I'll skip the UI change for input here, since your example is already there.
The last comment here, the controlled input does not have storage. So in order to use your input, you need to have a state defined from external.
const [result, setResult] = useState(0)
return (
<YourInput value={result} />
)
The take home message here is WHO should manage the storage (memory), this might be the #1 fixture to put into place before designing any component.