I'm working on a React app with a button to reset a form. When the user clicks the "Reset" button, all the states revert to their original values. But a checkmark remains selected even after all the other data clears. It does appear to reset the state, but the checkmark is still visible and confusing to the user. I understand that defaultChecked is read on the initial load, but the checkmark remains even if I remove defaultChecked completely, so I'm not sure where the problem is. I suspect the solution might involve onChange, but I haven't been able to get that to work. Any help appreciated.
const [checked, setChecked] = useState(false);
const clearSearch = () => {
setResults([]);
setQuery("");
setFormat("");
setChecked(false);
}
<label>
<input
type="checkbox"
id="searchAll"
defaultChecked={checked}
onChange={() => setChecked(!checked)}
/>
Search all formats
</label>
<button className="btn btn-danger"
onClick={event => {
event.preventDefault();
clearSearch();
}}>
Reset
</button>
App.js on GitHub https://github.com/irene-rojas/loc/blob/master/src/App.js
Your checkbox is actually an uncontrolled input, which probably isn't what you want (it usually isn't in React). That is because, although you've got the onChange event wired up to a handler which sets state, you're not reading back from that state into the input. Except in the defaultChecked attribute - which doesn't control whether it's actually checked or not.
You need to use the checked attribute, which for a checkbox is essentially equivalent to the value attribute of a text or numeric input:
<input
type="checkbox"
id="searchAll"
checked={checked}
onChange={() => setChecked(!checked)}
/>
(I've removed your defaultChecked attribute, as I don't think you want it - but feel free to put it back in if you do. The important thing is having a checked attribute that reads from the checked state value.)
Related
The problem: I have a selection of 3 radio buttons, and I need to allow users to unselect a radio button they have previously selected by clicking on it (toggle state based on user selection of radio button). Adding a "none" or "clear selections" button is not an option. So this needs to be either radio buttons that allow un-selecting, or checkboxes that allow only 1 selection.
How on earth do I do this in react cleanly? Semi-cleanly?
Disclaimers:
I have searched google and SO and found nothing that answers my questions, either b/c they don't solve my particular problem, or I am unknowingly misunderstanding them.
Yes I know this is not what radio buttons/checkboxes are designed to do, but this is what the client wants and I cannot push back on this decision any further
I'm very new to react, so apologies if the answer is obvious and I just missed it.
Mhm, this solution is not great, but, you can do the following:
onChange -> you just switch the state
onClick -> verify if the value is the one that you're already having in the state. If so, set the state to null.
Look at this CodeSandbox
Or, you can use a checkbox, and always keep 1 item in state at a time. And once that is clicked again, just clear the state.
import React from 'react'
const ExclusiveCheckBoxes = (props) => {
const allUnchecked = {option1:false, option2:false, option3:false};
const [options, setOptions] = React.useState(allUnchecked);
const chkChanged = e => {
setOptions({...allUnchecked, [e.target.name]: e.target.checked});
}
return (
<div>
<h1>ExclusiveCheckBoxes</h1>
<input type="checkbox" name="option1"
checked={options.option1} onChange={chkChanged}/>
<input type="checkbox" name="option2"
checked={options.option2} onChange={chkChanged}/>
<input type="checkbox" name="option3"
checked={options.option3} onChange={chkChanged}/>
</div>
)
}
export default ExclusiveCheckBoxes
im currently trying to create an dynamic form that uses a sub component to render out some input fields as createing them static would exceed a limit. I pass the states for the the form i made aswell as some date and the change form. But when i render the form as it does so fine! as soon as i change the state its sends a warning about uncontrolled components that you can read about it here
I have tried pre-setting all the fields with empty defaults but it doesnt help.
Am i doing it somewhat correct or totally wrong? or what is the correct way, or what am i doing wrong. thanks in adbance.
As for the code looks like this:
Edit.js
export default function InventoryItemEdit() {
const [form, setForm] = useState({});
function handleFormChange(e) {
setForm({ ...form, [e.target.name]: e.target.value });
}
const variants = (
<ItemVariants
form={form}
onChange={handleFormChange}
fields={form.sizes}
/* ^^ fields data fetched from the server i set via use effect hook when loading the page */
/>
);
const updateItem = async (event) => {
event.preventDefault();
/* Do form submit post */
};
return (
<form onSubmit={updateItem}>
<div>
<p htmlFor="name">Name</p>
<input
id="name"
name="name"
onChange={handleFormChange}
value={form.name}
type="text"
required
placeholder="Name of Item"
/>
</div>
{variants}
</form>
);
}
As for the sub component ItemVariants it looke like this
ItemVariants.js
export default function ItemVariants({
form = {},
onChange = '',
fields = [],
}) {
return (
<>
{fields.map((row, index) => (
<div>
<span>
{row.name}
</span>
<input
type="text"
id={`variant${index}`}
name={`variant${index}`}
onChange={onChange}
value={form[`variant${index}`]}
required
min="0"
placeholder="0"
defaultValue="0"
/>
</div>
))}
</>
);
}
Controlled component: is when your input value is coming from a single source of truth a state(meaning for an input value to change, you need to update the state holding the value)
Uncontrolled component: is when your input value is not coming from a state but rather it's coming from something we call useRef/createRef which helps to reference to react syntactic DOMNODE thus giving us the ability to interact with input in a false-actual-DOM(as if we were query an actual DOM input-node in pure-JS). Meaning if your input value is not coming from a state, it must come from a ref(ref.current.value which is equivalent to document.querySelector(theInputLocator).value).
Answer to your bug: I see that everything is okay, but the only issue is that you didn't set the initial state with the name key. Thats an analysing issue because in your code you doing [e.target.name]:e.target.value and the way inputs works generally is that event is triggered but must recent value is emitted when the event happens next. it works like a++.
Do this:
const [form, setForm] = React.useState({ name: '' });
The general rule of thumb is that always describe your form input keys when your inputs are predefine and not dynamic(in this case your name input is predefined).
So After digging reading this post i have to thank! #juliomalves for solution with value={form[`variant${index}`] ?? ''} or value={form[`variant${index}`] || ''}
I also found out i was accidently adding defaultvalue aswell as value which cannot be set at the same time i found out. After i tested it removed the default value and set it correctly to be a string! it now works like a charm!
Also thanks to #Emmanuel Onah for the explaination it really helped me understand more about the react states!
Thanks again for everyone that helped
I'm working with React JS and I want to make a hidden button for a text box, of type "Row", such that when I click onto that box, a description will appear on the side, the Row has this structure
<div>
<form key={id}>
<button
className="name"
type="submit"
onClick={clickFunc}
>
{data.text}
</button>
</form>
</div>
Where clickFunc makes a call to enterMouseFunc, a function passed in when called in another component file:
const clickFunc = useCallback((e) => {
e.preventDefault();
enterMouseFunc(e, data);
});
In the description box, there is an input field,
// some other details here
<form key={id} onClick={inputClick}>
<input
type="text"
className="inputParam"
name={id}
onChange={handleChange}
/>
<button
type="submit"
style={{ display: "none" }}
></button>
</form>
My inputClick:
const inputClick = (e) => {
e.preventDefault();
};
My handleChange:
const handleChange = (e) => {
e.preventDefault();
setParameters({ name: e.target.name, value: e.target.value });
};
Which works just fine. However, whenever I click on the Row again, or switch between other Rows, all user's inputs get cleared up. The page doesn't refresh since I already suppress this behavior, but the inputs still get deleted. How to stop it from clearing up the inputs all the time?
Edit:
This is how the interface looks like, where the grey boxes are of type Row. When we click on these boxes, the description will appear on the right as shown:
Are you by any chance triggering the onSubmit of the form? You prevent the inputClick but do not prevent the submit. However, im not sure it would be invoked. Why have a hidden submit button at all?
Does setParameters contain all of the fields values? If so you should copy the existing values before reassigning name and value. If not the issue is elsewhere.
A fix would look like this if that is the case:
const handleChange = (e) => {
e.preventDefault();
setParameters({...parameters, name: e.target.name, value: e.target.value});
};
Unless you would like to submit the form, then you don't necessarily need a <form> element?
Also, having a click event handler on that element (instead of say, a button) is not really common, I am not totally surprised that it causes side effects.
What I would do is remove forms that do not submit: just the standalone inputs that trigger setState/setReducer on change events.
Then you should be able to remove hidden buttons, preventDefault calls, etc.
About the cleaning of the values, I suspect your id value in the key prop to change, which would cause a re-mount of the form children.
I have implemented in React a webpage with 3 input fields, each one with the properties of
onChange={this.handleChange} and disabled={this.isDisabled()}
The desired behavior is that when an input field contains 2 digits, the focus will be moved to the next input field.
As long as the field doesn't contain 2 digits, the fields next to must be disabled.
What actually happens, when I type the second digit in the first field, it runs the handleChange function, that function checks whether the field contains 2 digits, find out that yes, and moves the focus to the next input field.
But the next field is disabled! (because the isDisabled function didn't run yet!)
So the cursor doesn't move..
I want to change the order of the happenings, or any other way to solve it.
Do you have any suggestions?
The problem is that this.isDisabled() runs immediately in render but this.handleChange runs on click and most possibly doesn't change the state thus no rerender.
You sholdn't run function on next input, you should pass true or false to its disabled prop. Just make handleChange update the state which defines which fields are disabled. And pass that state to your inputs accordingly.
I had faced the same issue a few days back. My approach was however using react states and focusing the input by its id, that was fetched from state;
So first we make a input - id map for our convenience. And use document.getElementById(this.state.active).focus() function. We change our state via our change handler.
render() {
this.setInputFocus();
return (
<div className="App">
<input id="1" onChange={this.onChange} />
<input id="2" onChange={this.onChange} />
<input id="3" onChange={this.onChange} />
</div>
);
}
setInputFocus = () => {
if (document.getElementById(this.state.active)) {
document.getElementById(this.state.active).focus();
}
};
onChange = e => {
if (e.target.value.length === 2) {
this.setState({ active: this.state.active + 1 });
}
};
Here is a full code that somewhat solves the issue
I am creating a custom button component in react. I want to pass a prop to that button, based on the value of which button gets enabled or disabled.
My problem is
- The mere presence of the disabled property disables the element, so I cannot set its value as "false". Even the following code is disabling the element
<input type="button" id="myBtn" value="Submit" disabled="" />
I need to either remove the attribute completely or set its property via javascript.
document.getElementById("myBtn").disabled = true;
My custom button component is -
import React from 'react';
const CustomButton = ({ whenClicked, classNames, buttonText, isDisabled }) =>
(
<button
onClick = {whenClicked}
className = {`btn ${classNames}`}
type = "button"
disabled = {isDisabled}
>
{buttonText}
</button>
);
export default CustomButton;
isDisabled is a boolean value.
One more thing, I lost the default submit behavior of button while using custom button. Now I always need to pass a click handler. Is there any way to achieve the same behavior again?
What you currently have should work perfectly fine. Be careful that when you use CustomButton you don't send in the value for disabled as a string. That will make it disabled regardless of what you pass in. Instead, you need to pass in a boolean, that's in JSX syntax.
Here's a sample usage where you would put it inside of a render function of a component that uses the button:
...
render() {
<div>
...
<CustomButton
whenClicked={() => console.log('I just got clicked')}
buttonText="Some Button"
isDisabled={false}
/>
...
</div>
}
...
Basically, instead of false you could pass in something else. You could pass in a boolean value that's stored on the props of the container that holds the CustomButton.
You could say isDisabled={this.props.disableInnerButton} and it would work as you would expect. Changing the value of the boolean will disable or enable the button.
Below you can find a relevant answer I gave recently on a very similar topic:
Statement to add or not an attribute in JSX
At first it looks quite complicated to handle but, if we look at the problem I think we can achive it using css only by adding a class(selector) to the element
.disabled { pointer-events: none;}
or conditionally styling:
<button style={{pointerEvents: notValid ? "none" : "initial"}}> Submit </button>