I have checkboxes that you can click as many as you want.
My problem is that it doesn't put a check after I uncheck. Also the values when I submit is not appearing in console.log
Pls check my codesandbox here
CLICK HERE
<Label htmlFor={id}>
<Input
type="checkbox"
id={name}
name={name}
value={value}
checked={checked}
onChange={onChange}
/>
<Indicator />
</Label>
<form onSubmit={handleSubmit}>
{products.map((product) => (
<div key={product}>
<Input
name={values.products}
value={values.products}
checked={values.products}
onChange={({ target }) => setFieldValue("products", target.value)}
/>
</div>
))}
<button type="submit">Submit</button>
</form>
when you are checking a checkbox your will get the value and check whether if the value is present already in the products list if it is not present then you should add the value ( this will be your check part ) else you can filter the value from the products ( this will be your uncheck ) .
<Checkbox
name="products"
value={product}
checked={values.products.includes(product)}
onChange={({ target }) => {
let updatedProducts;
if (values.products.includes(product)) {
updatedProducts = values.products.filter(
(product) => product !== target.value
);
} else {
updatedProducts = [...values.products, target.value];
}
setFieldValue("products", updatedProducts);
}}
/>
Working Sandbox
Sandbox
values.products is an array of your values, so it doesn't make sense to have this for your name or value. Instead, you need to pick out the specific value from the array that is relevant to your specific checkbox.
{products.map((product, index) => (
<div key={product}>
<p>{product}</p>
<Input
name={product}
value={values.products[index]}
checked={values.products[index]}
onChange={({ target }) => setFieldValue(`products.${index}`,!values.products[index])
}
/>
</div>
))}
Working Sandbox
Related
I'm trying to debug a react checkbox and the onChange event doesn't ever get called. I have added a console.log to test and this doesn't ever run. Here is the code for the checkbox. What is the issue?
return (
<div className="RampPane">
<div className="RampPane--content">
<p className="RampText">{transaction.merchant} </p>
<b>{moneyFormatter.format(transaction.amount)}</b>
<p className="RampText--hushed RampText--s">
{transaction.employee.firstName} {transaction.employee.lastName} - {transaction.date}
</p>
</div>
<InputCheckbox
id={transaction.id}
checked={approved}
disabled={loading}
onChange={async (newValue) => {
console.log("click")
await consumerSetTransactionApproval({
transactionId: transaction.id,
newValue,
})
setApproved(newValue)
}}
/>
</div>
)
Here is the InputCheckBox Component
return (
<div className="RampInputCheckbox--container" data-testid={inputId}>
<label
className={classNames("RampInputCheckbox--label", {
"RampInputCheckbox--label-checked": checked,
"RampInputCheckbox--label-disabled": disabled,
})}
/>
<input
id={inputId}
type="checkbox"
className="RampInputCheckbox--input"
checked={checked}
disabled={disabled}
onChange={() => onChange(!checked)}
/>
</div>
)
Use this for InputCheckBox Component
return (
<div className="RampInputCheckbox--container" data-testid={inputId}>
<label
className={classNames("RampInputCheckbox--label", {
"RampInputCheckbox--label-checked": checked,
"RampInputCheckbox--label-disabled": disabled,
})}
htmlFor={inputId}
/>
<input
id={inputId}
type="checkbox"
className="RampInputCheckbox--input"
checked={checked}
disabled={disabled}
onChange={() => onChange(!checked)}
/>
</div>
)
This works!
If the InputCheckBox is a custom built react component that renders an html input element try checking that the onChange handler is passed to the root component correctly
IF Not try including the component here to get a better insight
When clicking Reset, form should go back to initial values, but the Select element goes back to first option available, have used the same method for all other form elements, and they update correctly.
Have made a Codesandbox example of the problem : https://codesandbox.io/s/lively-snowflake-tf5te
function App() {
// Data for select dropdown box - selected is Lime
const SelectData = ["Grapefruit", "Lime", "Coconut", "Mango"];
// Selected Value
const selectedValue = "Lime";
// Set state
const [selectBox, setSelectBox] = useState(selectedValue);
// Submit finction
const handleSubmit = e => {
e.preventDefault();
alert(`Select Selection #1: ${selectBox}`);
};
// Reset function
const handleReset = () => {
setSelectBox(selectedValue);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>
When 'Reset' is clicked first options is selected
<br />
and not the state value.
</h2>
<form onSubmit={handleSubmit}>
<Select
label="Select Fruit"
value={selectBox}
handleChange={e => setSelectBox(e.target.value)}
data={SelectData}
/>
<br />
<br />
<input type="submit" value="Submit" />
<input type="reset" value="Reset" onClick={handleReset} />
</form>
<br />
Selectbox state value: {selectBox}
<br />
</div>
);
}
Expected result, after Resetting Form, is that then Select element value is "Lime", but it is "Grapefruit".
I changed the value to defaultValue in your DropDown.js file and it worked like charm. Check the sandbox
<select defaultValue={value} onChange={handleChange}>
{data.map(item => (
<option key={item} value={item}>
{item}
</option>
))}
</select>
Your code is correct. Your "bug" is due to the <input type='reset'/>.
Change it to a regular button and you'll see that it works just fine.
<button onClick={handleReset}>Reset</button>
https://codesandbox.io/s/dark-cdn-qchu5
From MDN: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/reset
elements of type "reset" are rendered as buttons, with a default click event handler that resets all of the inputs in the form to their initial values.
This is concurring with your controlled component.
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>
When 'Reset' is clicked first options is selected
<br />
and not the state value.
</h2>
<form onSubmit={handleSubmit}>
<Select
label="Select Fruit"
value={selectBox}
handleChange={e => setSelectBox(e.target.value)}
data={SelectData}
/>
<br />
<br />
<input type="submit" value="Submit" />
{/* <input type="reset" value="Reset" onClick={handleReset} /> */}
<button onClick={handleReset}>Reset</button>
</form>
<br />
Selectbox state value: {selectBox}
<br />
</div>
);
Even though select is a controlled component, Form doesn't support onReset event. Thus, the behavior is similar to $form.reset() which will reset the select value to default value when button of type=Reset is clicked.
So an alternative is to use defaultValue prop in select or use a normal button with onChange prop.
I have the following component that shows the options of a quiz, when the user select a value I want to pass the "item.id" selected in the "handleNext", no código abaixo aparece o seguinte erro:
Uncaught ReferenceError: item is not defined
code:
render () {
return (
<UserContext.Provider value={this.state}>
<UserContext.Consumer>
{({ current_statement, current_alternative, handleNext}) => (
<form>
<p>{current_statement}</p>
{this.state.current_alternative.map(item => (
<React.Fragment key={item.id}>
<div>
<input id={item.id} type="radio" name="question"/>
<label htmlFor={item.id}>{item.content}</label>
</div>
</React.Fragment>
))}
<button onClick={(e) => this.handleNext(e, item.id)} type="button">Next</button>
</form>
)}
</UserContext.Consumer>
</UserContext.Provider>
)
}
handleRadioChange = id => this.setState({selected: id})
render() {
...
<input id={item.id} type="radio" name="question" onChange={e => this.handleRadioChange(e.event.target.id)} />
...
<button onClick={(e) => this.handleNext(e, this.state.selected)} type="button">Next</button>
In short:
You have to store selected id of radio button selected by user in state, then your button will take id from state when the user presses it.
The problem is your binding of your Next button:
this.handleNext(e, item.id)
You've added this OUTSIDE of your .map() function, so there is no item (This is explicitly defined on each loop of your this.state.current_alternative map). Move this inside of the .map() array, and you'll be able to access item.
Hello I am trying to build a forum site. I have simple categories and comments. I have a button that displays a text box to enter a comment on a category, but when I click the button it opens text boxes on every category. I just want one text box. Here is the code I have so far. Any help would be appreciated. Thanks
state = { showing: true };
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
<button
className="label"
onClick={() => this.setState({ showing: !showing })}
>
Add Comment
</button>
{showing ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
render() {
return (
<div>
<main className="categories">
{this.renderLists(this.state.category)}
</main>
</div>
);
}
Since you are controlling render of the form with a single state value, ever form for every item renders the form when the state value changes.
You should set a unique value on state to show single form every time.
Saving currently active items id to state and checking if that item is active or not can be a simple solution. This also ensures to only single form to be active. If you need to enable multiple forms to be active, you can change below code to hold an array of ids and checking if the id exist in array or not. This implementation also requires you to remove the id from array on a second click to remove form for that item.
Sample
state = { showing: ''};
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
<button
className="label"
{/* Save item's id to state */}
onClick={() => this.setState({ showing: category._id })}
>
Add Comment
</button>
{/* check item's id */}
{(showing === category._id) ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
You can set the dynamic state for each of your category. I have made it below. Initially it was not there in state. On clicking the button, it will update the state with new key(such as categoryName) as true. I have fixed it as toggle. If the required key is true then it will become false.
item.state = { showing: true };
renderLists(categories) {
const { showing } = this.state;
if (categories == null) return <div />;
return (
<ul className="ul">
{categories.map(category => {
let categoryName = category.name // get the categoryName in variable
return (
<li id={category._id} className="categories" key={category._id}>
{category.name}
<Posts categ={category._id} />
//You can also access the object values by using bracket ([]) notation. You can set the dynamic state with unique key, may be we use name as key here
<button
className="label"
onClick={() => {(this.state[categoryName] != undefined) ? (this.state[categoryName] ? this.setState({ [categoryName]: false }) : this.setState({ [categoryName]: true })) : this.setState({ [categoryName]: true })} }
>
Add Comment
</button>
{this.state[categoryName] ? (
<div>
<form method="post" action="/post/create-posts">
<input type="text" name="body" />
<input type="hidden" name="cat" value={category._id} />
<input type="submit" value="Submit" />
</form>
</div>
) : null}
</li>
);
})}
</ul>
);
}
render() {
return (
<div>
<main className="categories">
{this.renderLists(this.state.category)}
</main>
</div>
);
}
I know that checkbox receive a bool value, so what can i do to change this with multiple checkbox.
below is where i have my all checkbox, the JSX file, each checkbox is a category and user can choose one or more categories
<div className='columns is-multiline'>
{this.props.propsCategories.data.list.map((category, i) => (
<div key={ i } className='column is-one-quarter'>
<Field
name='category[]'
label={ category.title }
component={ WrapperInputCheckbox }
setCategory={(e) => this.handleChange(e, category.id)}
/>
</div>
))}
</div>
in my handleChange i'm just trying change a single value.
//handleChange
handleChange = (e, id) => {
e.target.value = id
console.log(e.target.value);
}
but when i submit my form in category array i get
Category: Array[0]
"" : true
length : 0
the value still a bool :X
i need the value become a array like
category['id-1', 'id-n']
You can create a compound name in the id using the name of the input or passing a prop with other word.
Also you should use map, is useful when you want to apply the function to every item, also it's simpler and more concise than using a for loop to build a list.
<div className={classNameContentGroup}>
{ items.map((item) => (
<div className={`${classNameContent } `"}>
<input
{...input}
name={name}
type="checkbox"
value={ item.value }
className={className}
id={ `${name}-${item.value}` }
/>
<label className={classNameLabel} htmlFor={ `${ nameR}-${item.value}` }>
{item.label}
</label>
</div>
))
}
</div>