Getting values from checkbox group and adding/removing them from array - javascript

I am creating a form where I have a group of checkboxes.
When click on checkbox, I would get the value of that specific checkbox and add to an Array (I am using the useState hook) and if I uncheck it will remove that element from the array.
That's my code so far:
const ContactUs = () => {
const [cities, setCities] = useState([])
useEffect(() => {
console.log(cities)
})
const handleCheck = (e) => {
if (e.target.checked) {
setCities([...cities, e.target.value])
} else {
removeCities()
}
}
const removeCities = () => {
setCities(() => cities.splice(DT.length - 1, 1))
}
return (
<Content>
<form>
<SectionTitle>Day Tours</SectionTitle>
<Checkbox
type="checkbox"
id="1"
label="Dublin"
value="dublin"
name="dublin"
onChange={handleCheck}
/>
<Checkbox
type="checkbox"
id="2"
label="New York"
value="New York"
name="new-york"
onChange={handleCheck}
/>
<Checkbox
type="checkbox"
id="3"
label="Torino"
value="Torino"
name="torino"
onChange={handleCheck}
/>
</form>
</Content>
)
}
I can add it to the array but I can't seem to remove it (or remove the right one).
I tried splice and slice methods but as I don't fully grasp their concept.

I don't know what is DT as you haven't shared your whole code, but I think you can approach this in another still functional, declarative way:
Filter out the array so it returns everything that is checked and filters (removes) the unchecked ones:
in your else block:
setCities(cities.filter(city => city !== e.target.value))
Filter will always return a new array with the values that match the filter criteria, in this case it would return everything except the e.target.value, which is what we want as based on your logic, the else block will execute when it's unchecked.

Related

Svelte: Data flow between components - how to pass state of children to parent(s)

I haven't been using svelte for very long but I can see a couple ways to go about doing what I need, but they are all feel "un-svelte", so I'm hoping there's a more obvious solution I'm missing. Here is the layout code before I go on about what I need it to do.
<ControlGroup>
<ControlLabel label='Language'>
<Select options={lang_options} on:select={(e) => $settings.lang = e.detail} />
</ControlLabel>
<ControlLabel label='Visibility'>
<Select options={...} on:select />
<Select options={...} on:select />
</ControlLabel>
<ControlLabel label='Some other setting'>
<Select options={...} on:select />
<Select options={...} on:select />
</ControlLabel>
</ControlGroup>
First, the deepest nested elements (selects), need to update one or many stores. So far so good, the on:select directive makes quick work of that.
Second, the selects all have a defaultoption, which if none is provided will be options[0]. The <ControlGroup> needs to be aware if the selects within itself are default. Because when they aren't, a button to reset the selects should be visible, and clicking that should run the reset() function in the selects.
More broadly, the <ControlGroup> and <ControlLabel> components are there to structure the layout, they don't have any complicated logic. They use svelte slots to pass down components. Worth noting, I wrote the multiselects so it's not an opaque library and I can add stuff to them if that's part of the solution.
So without having a reactive let for every single control and then passing lists of those to the controlgroups, what can I do to pass the currently selected value of each select to their parents?
Thank you !
Edit, Current solution:
Top level
<ControlGroup name='General Settings'>
<ControlLabel label='Language'>
<Select id='lang' options={lang_options} selected={lang_current} on:select={(e) => $settings.lang = e.detail} />
</ControlLabel>
<ControlLabel label='Visibility'>
<Select id='visi' options={yesno} on:select={(e) => $settings.visibility = e.detail} />
</ControlLabel>
<ControlLabel label='Show Item Level'>
<Select id='ilvl' options={yesno} on:select={(e) => $settings.showItemLevel = e.detail} />
</ControlLabel>
</ControlGroup>
ControlGroup.svelte
let isDefault = true;
const checkDefault = () => {
for (var id in controls) {
if (controls[id].value === controls[id].defaultoption) continue;
return false;
}
return true;
};
const reset = () => {
for (var id in controls) controls[id].reset();
};
if (group) setContext(group, {
onmount: (id, reset, defaultoption) => controls[id] = {reset, defaultoption},
ondestroy: (id) => delete controls[id],
onselect: (id, value) => {
controls[id].value = value;
isDefault = checkDefault();
}
})
Select.svelte
const groupContext = getContext(group);
if (groupContext) groupContext.onmount(id, reset, defaultoption?.value);
$: if (groupContext) groupContext.onselect(id, selected?.value);
onDestroy(() => {
if (!groupContext) return;
groupContext.ondestroy(id);
});
I do not fully understand your question. But below I show how you can use get- and setContext to pass a value to the parent.
Parent.svelte:
import { setContext } from "svelte";
setContext("aParentId", (childId, value) => {
// function to show child notification in the parent
console.log(childId, value);
}
Child.svelte:
import { getContext } from "svelte";
const notifyParent = getContext("aParentId");
// notify parent if value changes
$: if (value) notifyParent(aChildId, value)

REACT-HOOKS: How do I store a modifed parameter made by the user?

I have checkboxes and I want to save in useState hooks the modified value made by the user. By default the current state is fixed and the checkbox is filled if my_value === 1, elif 0 unfilled. But if my user decides to uncheck it, how can I store this action. (if unchecked the value is 0).
Same idea with dropdown, the default value is fixed. The user can change the Taste( Good/Medium/Bad)or the Comments ((0/4....4/4)).
For now I get only the current state.
export default function Display() {
...
//For my checkboxes
const [availability, setAvailability] = useState(item.values[0].availability)
...
const [trust, setTrust] = useState(item.values[0].trust)
//For my dropdowns
const [taste, setTaste] = useState(item.taste)
...
const [comments, setComments] = useState(rule.comments)
function Checkbox({ value }) {
const [checked, setChecked] = useState(value);
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(checked => !checked)}
/>
{value}
</label>
);
}
return (
<div>
<div>
Availability : <Checkbox value={!!availability} />
</div>
....
<div >
Taste : <Dropdown style={styles.select} options={TASTE} defaultValue={LIKELIHOOD.find((t) => t.label === item.taste)} />
</div>
...
</div >
);
}
This isn't so much a hooks problem as a "where do I store my state" problem. So far I don't see any place in your implementation to store the users choices. Either the MenuItemDisplay component needs to maintain that state, or it needs to receive it from a parent component. Either way, that state (containing user choices) will need to be passed down (along with update functions) into the checkbox component as the value of a 'checked' prop, and the update functions for that state should be passed as (and adapted to) the checkbox 'onToggle' (or similar) prop

How to use useEffect inside a handler or vice versa?

I have a function which is handler of onChange and get value of input
and also I have a function for search in an array using searched value coming from that input
and render a component that contains searched array as props.
But I have a problem
when I search it works but after second letter and when the input is empty it shows the last search.
I think it should be handled with useEffect but I dont how to solve or may be I am wrong
I need help to correct that
thanks for your help.
these are my code :
getting search value part and sending as argument :
function BasePage({handleClick, handleSearch }) {
const [searchValue, setSearchValue] = useState('');
useEffect(() => {
function handleChangeSearchInput(e) {
const newSearchValue = e.target.value;
setSearchValue(newSearchValue);
handleSearch(searchValue);
}
})
return (
<div>
<fieldset>
<legend>Search</legend>
<input
value = {searchValue}
onChange = {handleChangeSearchInput}
placeholder = "Enter name to search"
type = "text"
/>
<button onClick={() => handleClick('add-record-form')}>Add</button>
</fieldset>
<br />
{searchValue.length > 0 && <SearchRecord record = {searchValue} />}
</div>
);
}
and this filters in parent component :
function handleSearch(searchValue) {
const searchedTerm = contacts.filter(contact => (contact.name.toLowerCase().includes(searchValue.toLowerCase())));
setSearchTerms(searchedTerm);
}
and I use map to show them .
You shouldn't need useEffect for this. Just have the handler deal with both the setting of state, and the updating.
function handleChangeSearchInput(e) {
const newSearchValue = e.target.value;
setSearchValue(newSearchValue);
handleSearch(newSearchValue);
}

onChange is appending data multiple times when checking for a string character

I'm trying to append a drop down value to a textarea field, it works as a mention. So if there is an "#" call the dropdown and user will select a user, once selected the dropdown show hide it self, and the user should be able to append their comment data.
the issue im having is that setCommentBody is appending the selectedUser multiple times/ on every comment change in the
textarea
My objective is
check for # symbol (which its already doing)
render drop down once # symbol is called( which is already doing)
once a value is selected hide drop down and add their comment (dropdown hides only if # symbol is removed, which it should hide after a value is selected)
The mention should pretty much work exactly how stackoverflow comment section has it.
this is what i have so far
const [comment_body, setCommentBody] = useState("");
const [mentionedUser, setMentionedUser] = useState(false);
const commentChange = (comment) => {
console.log("this is the selected User", selectedUser); // selected user is a reducer initalState
// call this condition if # is mentioned once
if (comment.includes("#")) {
setMentionedUser(true); // render dropwdown
setCommentBody(comment.concat(selectedUser)); // append the selected user like #barnowl with the respective comment data
} else {
console.log("can you see me");
setMentionedUser(false); // hide dropdown
setCommentBody(comment);
}
setGifUrl(""); // irrelvant to the problem ignore
};
PostItemContainer
<CommentForm
commentChange={(e: any) => commentChange(e.target.value)}
comment_body={comment_body}
onSubmit={(e) => commentSubmit(e, post.id)}
gifUrl={selectGif}
isGif={gifUrl}
mentionedUser={mentionedUser}
/>;
CommentForm (snippet)
....
<OurTextField
type="gif-commentfield"
selectedUser={selectedUser}
comment_body={props.comment_body}
commentChange={props.commentChange}
setGifSelected={() => setGifSelected(true)}
/>;
{
props.mentionedUser && (
<select
value={selectedUser}
onChange={(e) => setSelectedOptionValue(e.target.value)}
name="mentionedUsers"
>
{mentionUsers.map((item, key) => (
<option key={key} value={item.display}>
{item.display}
</option>
))}
</select>
);
}
A minimal working example of my issue
https://codesandbox.io/s/practical-ives-lfckq?file=/src/App.js
If I understand the use case correctly, the name insertion logic should be triggered when the selection field is changed:
const commentChange = (comment) => {
setCommentBody(comment);
if (comment.includes("#")) {
showMentionList(true);
}
};
const selectedUserChange = (user) => {
setSelectedUser(user);
setCommentBody(commentBody.concat(user).replace("#", ""));
showMentionList(false);
};
...
<select
value={selectedUser}
onChange={(e) => selectedUserChange(e.target.value)}
name="mentionedUsers"
>
{users.map((item, key) => (
<option key={key} value={item}>
{item}
</option>
))}
</select>
Does this work for you? See: https://codesandbox.io/s/broken-haze-qwb41?file=/src/App.js

How to use checkbox as Radio Button in ReactJS with hooks?

I'm Building a filter in ReactJS with hooks and now I need apply some style to the checkboxes.. The CSS is not the problem, I already did it in vanilla:
function checkOne(checkbox) {
var checkboxes = document.getElementsByName('plan')
checkboxes.forEach((item) => {
item !== checkbox ? item.checked = false : item.checked = true;
})
}
=======================================================================
X 3
<label for="premium">
<input
type="checkbox"
name="plan"
onclick="checkOne(this)"
class="input-check"
value="Premium"
>
<span
class="checkmark"
id="premium"
>
Premium
</span>
</label>
Now I need to do the same think in react with hooks and I get stuck, I'm mapping a source of products and making a list of checkboxes form the product categories...
...
const [checked, setChecked] = useState(false) //hook
...
handleChange(e, checkbox){
setProduct(e.target.value);
setSearch(e.target.value);
let checkboxes = document.getElementsByName('products')
checkboxes.forEach((item) => {
item !== checkbox ? setChecked(false) : setChecked(true);
})
}
... in render method
<div className="filters" id="top">
{uniqueProduct.map(product => (
<label key={product.id}>
<input
className='filters-available-size'
type="checkbox"
value={product.category}
onChange={handleChangeProduct} // <-- ?¿?¿ what should go here?
name='product'
/>
<span className="checkmark">
{product.category}
</span>
</label>
))}
</div>
Why do you need to use checkboxes as radio buttons? I'm going to assume because of the styling--a design decision, perhaps. In which case, I would use radio buttons for the functionality and then use CSS to hide radio buttons and show checkboxes that reflect the state of the chosen option.

Categories

Resources