<section key={i}>
<input
type='radio'
key={attribute.name + item.id}
id={attribute.name + item.id}
name={attribute.name}
value={item.value}
defaultChecked={i === 0}
onClick={(event) => inputClick(attribute, event)}
/>
<label
htmlFor={attribute.name + item.id}
>
{
item.value
}
</label>
</section>
The code above is more or less what a .map() function is supposed to return in my React.js APP, creating input radio buttons for customizing a product before adding it to the cart state. All I did was remove some classes, etc. that were there for CSS purposes, etc.
Ideally, what is supposed to happen here is that...
When rendering the product page for the first time, the very first input radio button that is returned by .map() should be checked by default. This works thanks to the "defaultChecked" attribute. No problems here.
After I click a different input radio button (not the "defaultChecked" one), the checked input should change and all the other inputs should be left unchecked. As I understand it, input radio buttons with the same 'name' attribute do this automatically.
When clicking the input radio button, the "onClick" function should trigger. This is where the code is not functioning as I wish it would.
The issue is that when my page renders for the first time and I click on an input radio button (other than the "defaultChecked") the "onClick" function does not trigger. It only triggers when I have clicked on a different input once and then on another.
A.k.a. it triggers on the second click. After that second click, it works as intended - every time a different input radio button is selected/clicked on - the function triggers, but not for the very first time.
I tested this with console.log("I am triggered") at the end of the "onClick" "inputClick(attribute, event)" function, and only on the second click would the console log "I am triggered".
I was able to fix the issue by removing the "defaultChecked" attribute. I think the issue might be tied to the fact that the "onClick" function is only able to be triggered when one input gains the "checked" attribute and another loses it, but the "defaultChecked" attribute does not count as an input being "fully checked" or something like that.
I could leave it at that, but the project that I am working on required me to have a default checked input radio button on the first-page render. So, I can't just delete the "defaultChecked" attribute and call it a day.
Any ideas on what could be causing this behavior?
UPDATE1
The following is the body of the inputclick() function:
//* Handle input selection
const inputClick = (attribute, event) => {
//* Parse state
let state = JSON.parse(JSON.stringify(itemState));
//* Get all the required values from the clicked input
const attributeId = attribute.id;
const itemTargetValue = event.target.value;
//* Check if the attribute is in state
const attributeIs = state.some(item => item.id === attributeId);
//* If the attribute does not exsist - add the attribute to state
if(attributeIs === false) {
const obj = {
id: attributeId,
selectedItem: itemTargetValue
};
state.push(obj);
return setitemState(state);
}
//* If the attribute id already exsists in state
if(attributeIs) {
//* Find the index of the attribute in question
const attributeIndex = state.map(object => object.id).indexOf(attributeId);
const attributeInQuestion = state[attributeIndex].selectedItem;
//* If the attribute's item's id is the same as the seelected input - do nothing
if(attributeInQuestion === itemTargetValue) {
return
}
//* If the attribute's item's id is not the same - change it to the new value
if(attributeInQuestion !== itemTargetValue) {
state[attributeIndex].selectedItem = itemTargetValue;
console.log(state);
return setitemState(state);
}
}
};
Here is the working code that fixes the issue.
Yes, there is some streamlining, for example, code shortening, etc. Yet, the difference that solves the issue is that...
The code that I had posted in the question originally was working. Meaning, that it was firing the inputClick() function and changing which input was selected, the problem was that...
...the defaultChecked logic in the was preventing the chosen input from being rendered as a selected input a.k.a. to change its CSS styling.
Bellow is the new onClick() function.
//* Handle input selection
const inputClick = (product, attribute, event) => {
let newCart = JSON.parse(JSON.stringify(cartState));
const productId = product.id;
const attributeId = attribute.id;
const itemTargetValue = event.target.value;
//* Find the product in cart state */
const productIndex = newCart.map((object) => object.id).indexOf(productId);
//* Find the attribute by id in question */
const attributeIndex = newCart[productIndex].selectedAttributes.map(object => object.id).indexOf(attributeId);
//* Change the products selected attribute item */
newCart[productIndex].selectedAttributes[attributeIndex].selectedItem = itemTargetValue;
setcartState(newCart);
};
Below is what the "inside" of the looks like now.
<input
type='radio'
key={product.id + attribute.name + item.id}
id={product.id + attribute.name + item.id}
name={product.id + attribute.name}
value={item.value}
defaultChecked={product.selectedAttributes[selectedId].selectedItem === item.value}
onChange={(event) => inputClick(product, attribute, event)}
>
</input>
Related
Having a hard time seeing how I could accomplish this. I created some custom number buttons from 0-9 that users can click on instead of using the keyboard. The problem I'm having is I have multiple dynamically created input fields depending on JSON Data so let's say there are 10 dynamically created input fields and a user starts with question one and the user then uses the custom number buttons I created and clicks numbers "145" to answer question one, but what happens is then all 10 inputs have the same number "145" not the problem the user was trying to solve. I'm using the context API to then save the values typed in on a function called getButtonValue that I then call to the parent component and save the values in a state array, so I know that my problem is that all the inputs share the same state array but how could I make sure the correct input the user clicks on is only receiving those values.
Thanks in advance.
My Custom Number Button Component:
import { FormContext } from "../../lib/FormContext";
function ActivityBar() {
const { getButtonValue } = useContext(FormContext);
return (
<div className={`${activity.activity__workSheet__numberButton}`}>
<button value={0} onFocus={(e) => getButtonValue(e)}>
<img
className={`${activity.activity__workSheet__img0}`}
src={"/assets/activityNumber-btn.png"}
alt="activity number button"
/>
.... more code
Parent Component:
const [numberButtonClicked, setNumberButtonClicked] = useState([]);
const getButtonValue = (e) => {
setNumberButtonClicked((prevButtonClicked) => [
...prevButtonClicked,
e?.target?.attributes[0].value
]);
};
return (
<Carousel>
<div ref={imageRef} style={{ height: "100%" }}>
{Object.entries(elements).map((element, i) => {
const { fields } = element[1];
if (fields) {
return (
<Element
key={i}
field={fields[0]}
id={i}
useReff={`answer${i}`}
currentValue={
numberButtonClicked === "" ? null : numberButtonClicked.join("")
}
/>
);
} else {
return;
}
})}
</div>
</Carousel>
Got a good working version figured out for this scenario, what I did was.
I have a onFocus method on my input tags that then takes in the event and calls a handleChange(e) function. Within that function I then save the currentInputId in a variable by using e?.target?.attributes[0]?.value and the previous InputId in a state variable and just check if the previous InputId is equal to the currentId user just focused on. If so then we'll add the next number user clicks into the same field, else if previousInputId !== currentInputId then make my user value state array empty, setNumberButtonClicked([]).
currently I have a signup form with 5 options but I'm trying to find a way to limit so the user can only select 2 options and in case the user selects a third option the first one would be unchecked, I had found a way of doing this in plain js but I haven't found a react way of doing it. This is what I have so far, would it be better to handle with plain js instead of react?
{iconsPool.map((src, index) => (
<Box className="test">
<input type="checkbox" className="iconsCheckbox" id={iconsPool.id} />
<label for={iconsPool.id}>
<img className="signupIcons" src={iconsPool[index].src} key={index} />
</label>
{console.log(iconsPool)}
</Box>
))}
This can be implemented with a state as an array with 2 elements.
Two items of the state Array will represent the index of selected items.
If an checkbox is clicked, that checkbox and the one clicked right before will be checked. (Therefore unchecking the one that was clicked even before that)
This can be done by pushing the index of newly clicked checkbox into the head of array, and removing the last item of the array.
When an checked checkbox is clicked again, (therefore it should be unchecked,) the index of the checkbox is searched from the state array, and removed by replacing that value with undefined
Below is code, as an example
...
const [checkedItems, setCheckedItems] = useState([undefined,undefined])
// When an Item is clicked
const onClickItem = (itemIndex:number, isSelected: boolean) =>{
if(isSelected){
setCheckedItems(c=>([itemIndex,c[0]]))
} else {
if(itemIndex === checkedItems[0]){
setCheckedItems(c=>([undefined,c[1]]))
} else if(itemIndex === checkedItems[1]){
setCheckedItems(c=>([c[0],undefined]))
}
}
}
I am writing a trivia app. When I select an answer, I check if it is the right one and get a new question right after. The problem is that I can't get the React to render the selected radio button when it is the wrong answer.
Screenshot of what it looks like
I am generating the Trivia questions from a JSON file. I have to make a input and a label tag dynamically. This is what I have so far:
I can get my next question when the selected one is right, but it doesn't show any radio selected. My isRadioChecked starts with false. I also tried checked={this.state.isRadioChecked == object}.
Below, I check if the clicked radio was the right answer and my render method
checkResult = event => {
this.setState(
{
selectedAnswer: event.target.value
},
() => {
if (this.state.selectedAnswer === this.state.correctAnswer) {
this.setState(
{
isRadioChecked: false,
currentQuestionNumber: this.state.currentQuestionNumber + 1
},
() => {
this.getNewQuestion();
}
);
render() {
return (
<div onChange={this.checkResult}>
<h2>Question Component</h2>
<h3>{this.displayQuestion()}</h3>
<div>{this.displayAlternatives()}</div>
</div>
);
}
}
I really appreciate your help. Thank You!!
[EDIT]
Thank You, Arpitha that was the first thing I tried and actually my last attempts came straight from http://react.tips/radio-buttons-in-reactjs/ . I think the issue is that I am creating the radios using dynamic data.
Anyways, I did change it to like it was before and still, I get no visible selected radio, even though the log shows the selected answer.
setUpAnswers() {
let tempAnswerArray = this.mergeAnswers(this.state.currentQuestionNumber);
tempAnswerArray = tempAnswerArray.map((object, i) => renderHTML(object));
console.log(tempAnswerArray[0]);
tempAnswerArray = tempAnswerArray.map((object, i) => (
<div key={i}>
<input
type="radio"
name="answers"
checked={this.state.selectedAnswer === object}
onChange={this.handleOptionChange}
id={object}
value={object}
/>
<label htmlFor={object}>{object}</label>
</div>
));
this.setState({
answerArray: tempAnswerArray
});
}
//*************************************************** */
handleOptionChange = changeEvent => {
this.setState(
{
selectedAnswer: changeEvent.target.value
},
() => {
console.log(this.state.selectedAnswer);
}
);
};
You are assigning this.state.isRadioChecked status to all the radio buttons like this checked={this.state.isRadioChecked} . So all the radio buttons will be checked or unchecked at the same time.
Instead you can use state.selectedAnswer to update checked attribute of radio button. Something like this -
checked={this.state.selectedAnswer === object}
A nice article on how to create radio groups is explained here - http://react.tips/radio-buttons-in-reactjs/
In a complex tabbed form in react-admin I need to have two submit buttons, one is the regular save button and one for altering the "status" field (advancing one workflow step) and saving the form.
The save butten should only become active if all required fields are filled by the user.
The other button changes its text depending on a "status" field in the record which contains the current workflow step, and is only active when the form validation for the current workflow step passes.
So either I need a dynamic button or several buttons which show and hide depending on the "status" field.
I think the dynamic button would be the more elegant solution.
Below you see the code I currently have, it is more or less copied from the react-admin documentation. I need to add a custom save button as well, but it is just a subset, easy to do when the AdvanceWorkflowButton works at the end.
const AdvanceWorkflowButton= ({ handleSubmitWithRedirect, ...props }) => {
const [create] = useCreate('posts');
const redirectTo = useRedirect();
const notify = useNotify();
const { basePath, redirect } = props;
const form = useForm();
// I need to set the label dynamically ... how?
// I also need sth like:
// if (validationSucceeds()) enable=true
const handleClick = useCallback(() => {
// here I need to check the current content of the "status" field.... how?
form.change('status', { "id": 2, "name": "Vorbereitung begonnen" });
handleSubmitWithRedirect('list');
}, [form]);
return <SaveButton {...props} handleSubmitWithRedirect={handleClick} />;
};
const CustomToolbar = props => (
<Toolbar {...props} >
<SaveButton
label="Speichern"
redirect="list"
submitOnEnter={true}
variant="text"
/>
<AdvanceWorkflowButton />
</Toolbar>
);
I had the exact same trouble.
Needed a button to save the form without validation, and another to save and change status with validation in place.
The code above helped me get to the answer, here are my configuration of the components necessary to achieve the desired outcome.
Set a new truthy value up in the form data as follows when the user clicks the save and next. Check the new property ('goNextStep' in our example) on the server to move the process forward.
<SaveButton
label="Save and next step"
handleSubmitWithRedirect={() => {
form.change('goNextStep', 1); // or true
props.handleSubmitWithRedirect('list');
}}
</SaveButton>
<SaveButton
label="Save only"
handleSubmitWithRedirect={() => {
form.change('validateCustom', 0); // or false
props.handleSubmitWithRedirect('list');
}}
/>
Use the validate prop on react-admin form. I could not make it work with field level validations. I had to remove every field level validation props, and implement all those in validateFunction.
Altough, you could still use the validators in your custom validation function.
const validateFunction = (values) =>{
// using our previously set custom value, which tells us which button the user clicked
let shouldValidate = values.goNextStep === 1;
// return undefined if you dont want any validation error
if (!shouldValidate) return undefined;
let errors = {};
// use built in validations something like this
var someTextFieldErrorText = required()(values.someTextField, values);
if (someTextFieldErrorText) {
errors.someTextFieldErrorText = someTextFieldErrorText;
}
// OR write plain simple validation yourself
if(!values.someTextField) {
errors.someTextField = 'Invalid property!';
}
return Object.keys(errors) ? errors : undefined;
}
Than set up tabbed form to use the previous function for validation.
<TabbedForm
validate={validateFunction}
>
...
</TabbedForm
React-admin version: 3.10.1
I render multiple checkboxes for a filter overlay. Now i want to clear all the checkboxes on a click of a button. How can i do this?
I render the checkboxes like this:
{
filter.Choices.map((choice: any) => (
<Checkbox
title = {filter.InternalName}
label = {choice}
onChange = {this._makeChangeHandler(choice, filter.InternalName, filter.Id)}
/>
))
}
Here is how i render my clear filter button:
<DefaultButton className={style.overlayClearButton} onClick={this._clearFilters}>Clear Filters</DefaultButton>
I want to uncheck all the checkboxes when i click the "Clear Filters" button.
you can use a controlled Checkbox for this. It´s basically the same, but you control the value of your checkbox on your own. Store this with your filter.Choices objects
filter.Choices.map((choice: any) => (
<Checkbox
title = {filter.InternalName}
label = {choice.label}
onChange = {this._makeChangeHandler(choice, filter.InternalName, filter.Id)}
checked={choice.isChecked}
/>
))
And on your button click you can now unset all checkboxes:
//Clear isChecked values of choices and set state for rerender
public _clearFilters() {
this.setState(state => ({...state, filter: state.filter.Choices.map(choice => {choice.isChecked = false; return choice;}))});
}
The solution depends on how you handle state in your application. But the best way is to control the checkbox state yourself.
I hope this helps,
greetings joe
for more sharepoint related help look at https://www.smarterbusiness.at/smarter-blog