Disable all checkboxes within a array in a row - javascript

I am trying to disable a set of checkbox within a row. For example,
I have the following
{availableBendsHostnames?.map((bEnds, indx) => (
<div key={indx}>
<div>B-End: {bEnds}</div>
<div>
<div>
<div>
{responseMessage !== 'success' &&
listOfEvenIpsWithCorrectSubnet?.map((ipAddress, idx) => (
<div key={idx}>
<FormControlLabel
control={
<Checkbox
name={`ip-${ipAddress}-${indx}`}
id={`ip-${ipAddress}-${indx}`}
value={ipAddress}
disabled={!isChecked[idx] && isDisabled}
onChange={(e) => handleIPSelected(e, idx, indx)}
/>
}
label={ipAddress}
/>
</div>
))}
</div>
</div>
</div>
</div>
))}
this out put as the following
row a
checkbox0 (name = ip-11.0.0.16/31-0, index = ip-11.0.0.16/31-0),
checkbox1 (name = ip-11.0.0.18/31-0, index = ip-11.0.0.18/31-0),
checkbox2 (name = ip-11.0.0.20/31-0, index = ip-11.0.0.20/31-0)
row b
checkbox0 (name = ip-11.0.0.16/31-1, index = ip-11.0.0.16/31-1),
checkbox1 (name = ip-11.0.0.18/31-1, index = ip-11.0.0.18/31-1),
checkbox2 (name = ip-11.0.0.20/31-1, index = ip-11.0.0.20/31-1)
row b
checkbox0 (name = ip-11.0.0.16/31-2, index = ip-11.0.0.16/31-2),
checkbox1 (name = ip-11.0.0.18/31-2, index = ip-11.0.0.18/31-2),
checkbox2 (name = ip-11.0.0.20/31-2, index = ip-11.0.0.20/31-2)
What I would like to happen is when the user checks on
checkbox0 (name = ip-11.0.0.16/31-0, index = ip-11.0.0.16/31-0)
I would like to disable the other checkbox in that row.
Here is what I have so far,
in my handle selected method I have the following
const [isChecked, setIsChecked] = useState({ checked: {} });
const [isDisabled, setIsDisabled] = useState();
const handleIPSelected = (selectIPIndx,) => {
setIsChecked((previousState) => ({
checked: {
...previousState.checked,
[selectIPIndx]: !previousState.checked[selectIPIndx],
},
}));
}
And I have a useEffect
useEffect(() => {
const checkedCount = Object.keys(isChecked).filter(
(key) => isChecked[key] && Object.keys(isChecked[key]).length !== 0
).length;
const disabled = checkedCount > 0;
setIsDisabled(disabled);
}, [isChecked]);
At the moment with this code as soon as I click on one checkbox it disables all checkboxes not just the one in the row. Any help would be appreciated.

You need to save for each checked item it's row.
There are also small errors, I fixed them to make it work:
const [isChecked, setIsChecked] = useState({});
const [disabledRows, setDisabledRows] = useState([]);
useEffect(() => {
const disabledRows = Object.keys(isChecked)
.filter((key) => isChecked[key].checked)
.map((key) => isChecked[key].row);
setDisabledRows(disabledRows);
}, [isChecked]);
const responseMessage = "error";
const availableBendsHostnames = [1, 2, 3];
const listOfEvenIpsWithCorrectSubnet = [4, 5, 6];
const handleIPSelected = (e, idx, row) => {
const { id } = e.currentTarget;
setIsChecked((previousState) => ({
...previousState,
[id]: {
checked: !previousState[id]?.checked,
row
}
}));
};
You can refer to this Code Sandbox working example

Related

Modifying Hook setState for an Array in React using Splice

I'm trying to splice an array hook State, so I can remove items from my list. I know you're not supposed to modify any hook states directly, so I made a copy into array2 before settting it, but it's removing all items below the one I click, instead of just the one I click. Any ideas?
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const [array, setArray] = useState([]);
let array2 = [];
const name = ({ target }) => setItem(target.value);
const remove = ({ target }) => {
for (let x = 0; x <= array.length; x++) {
if (array[x] === target.value) {
array2 = list;
array2 = array2.splice(x, 1);
}
setList([...list, array2]);
}
};
const create = () => {
if (item !== 'Default' && document.getElementById('box').value !== '') {
let value = document.getElementById('box').value;
setArray([...array, value]);
setList([
...list,
<div>
<p>
{' '}
{item} Qty: 1<button>+</button>
<button>-</button>
<button
value={document.getElementById('box').value}
onClick={remove}
>
x
</button>
</p>
</div>,
]);
} else {
alert('Please enter an Item');
}
document.getElementById('box').value = '';
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={create} />
<input type='button' value='Clear' onClick={clear} />
{list}
</div>
);
}
You should just store the strings in the list array and display the elements using a map function:
export default function Shopping() {
const [item, setItem] = useState('Default');
const [list, setList] = useState([]);
const name = ({ target }) => setItem(target.value);
const remove = (index) => {
const filteredList = [...list].splice(index, 1);
setList(filteredList);
};
const create = () => {
if (item !== 'Default' && item !== '') {
setList([...list, item]);
} else {
alert('Please enter an Item');
}
setItem('Default');
};
const clear = () => setList([]);
return (
<div>
<input type='textbox' id='box' onChange={name} />
<input type='button' value='Add' onClick={() => create()} />
<input type='button' value='Clear' onClick={() => clear()} />
{list.map((it, index) => {
return (
<div key={index}>
<p>
{' '}
{it} Qty: 1<button>+</button>
<button>-</button>
<button value={it} onClick={() => remove(index)}>
x
</button>
</p>
</div>
);
})}
</div>
);
}
```
Just Update the code in remove function because array2 = list also copies the reference of list array so when you update the array2 it also update the list array.so replace the remove function code with given code below .
if ((array[x])===target.value){
array2 = list.map(item=>item)
array2 = array2.splice(x,1)
}

React Select All Checkbox not working properly

I am trying to implement Select All check box functionality to delete all/ multiple posts in one click
React Component:-
function AllPosts() {
const dispatch = useDispatch();
const [page, setPage] = useState(0);
const { posts } = useSelector((state) => state.posts);
const [deletePosts, setDeletePosts] = useState([]);
const [postsData, setPostsData] = useState([]);
const { totalPages } = posts;
// get posts
useEffect(() => {
dispatch(getPosts(page));
}, [dispatch, page]);
// setting posts state
useEffect(() => {
setPostsData(posts?.Allposts);
}, [posts]);
// ***********************************************
// * *
// * Problem Part *
// * *
// * *
// ***********************************************
const handleAllChecked = (e) => {
// checkbox name is id of the posts
const { name, checked } = e.target;
// update list for IDs of selected checkbox
let updateList;
if (name === "CheckAll") {
// cant add post._id as it shows undefined
// deletPosts is an array of Ids to delete.
postsData?.map((post) => [...deletePosts, post._id]);
// check all checkboxes
let tempPost = postsData?.map((post) => {
return { ...post, isChecked: checked };
});
setPostsData(tempPost);
} else
{
let tempPost = postsData?.map((post) =>
post._id === name ? { ...post, isChecked: checked } : post
);
// add IDs of posts to delete to deletePosts Array
if (deletePosts.includes(name)) {
updateList = deletePosts.filter((item) => item !== name);
} else {
updateList = [...deletePosts, name];
}
// set deletePosts array
setDeletePosts(updateList);
setPostsData(tempPost);
}
};
// Delete Posts
const handleDelete = () => {
dispatch(deletePost(deletePosts));
};
return (
<>
<div className="all-posts-wrapper">
<div className="all-posts-main-container">
<ActionBar>
{/* *******************************************
*
* Check Box and Delete Button component
*
*
*************************************************/}
<ActionBarBtns
checked={
!postsData?.some((post) => post?.isChecked !== true)
}
name="CheckAll"
onChange={handleAllChecked}
handleDelete={handleDelete}
/>
</ActionBar>
<div className="all-posts-main-content">
<div className="all-posts">
<>
{typeof postsData === typeof [] && (
<>
{postsData.map((post, index) => (
<Post
key={post._id}
postId={post._id}
postSubject={post.subject}
checked={post?.isChecked || false}
onChange={handleAllChecked}
checkBoxName={post._id}
/>
))}
</>
)}
</>
</div>
</div>
</div>
</div>
</>
);
}
export default AllPosts;
The problem part of the code
const handleAllChecked = (e) => {
// checkbox name is id of the posts
const { name, checked } = e.target;
// update list for IDs of selected checkbox
let updateList;
if (name === "CheckAll") {
// cant add post._id as it shows undefined
// deletPosts is an array of Ids to delete.
postsData?.map((post) => [...deletePosts, post._id]);
// check all checkboxes
let tempPost = postsData?.map((post) => {
return { ...post, isChecked: checked };
});
setPostsData(tempPost);
} else
{
let tempPost = postsData?.map((post) =>
post._id === name ? { ...post, isChecked: checked } : post
);
// add IDs of posts to delete to deletePosts Array
if (deletePosts.includes(name)) {
updateList = deletePosts.filter((item) => item !== name);
} else {
updateList = [...deletePosts, name];
}
// set deletePosts array
setDeletePosts(updateList);
setPostsData(tempPost);
}
};
My Intention:-
whenever any Checkbox is selected its Id should be added to the deletePosts
array
whenever the Select All checkbox is checked all posts Ids should be added to
deletePosts array
Code's current working:-
If any or all posts checkbox is checked then their IDs are added to deletePosts array.
This Works
If the Select All checkbox is checked then all posts IDs are not added to deletePosts
the deletePosts array is undefined when Select All is checked.

Adding and removing objects in state of a component

I have a list of check inputs, and when they are selected and deselected I am trying to add/remove them from the state. But Ive noticed when I deselect one the one I selected prior is the object removed from the state. I think this is because when the onChange function is called the state hasnt updated (although the UI has) but I dont understand how to have a full list of all the checks selected with the state lagging behind one value. Heres my input and onChange func:
const [selected, setSelected] = useState([]);
const handleSelect = (e) =>
!!DATA &&
DATA.forEach((item, idx) => {
let name = e.target.name;
if (item.payerName === name && !selected.includes(item)) {
setSelected([...selected, item]);
return;
} else if (item.payerName === name && selected.includes(item)) {
// index varies
let index = selected.indexOf(item);
let clean = selected.splice(index, 1);
setSelected(clean);
}
});
DATA.map((item, idx) => {
return(
<input
name={item.payerName}
type="checkbox"
checked={selected.includes(item)}
onChange={handleSelect}
className="insurance-checkbox m-2 mt-3"
/>
)
}
Try this:
const [selected, setSelected] = useState([]);
const handleSelect = (e) => {
const { name } = e.target;
const item = DATA.find(({ payerName }) => payerName === name);
if (selected.includes(item)) {
setSelected(selected.filter((s) => s === item);
} else {
setSelected([...selected, item]);
}
}
or even better:
const handleSelect = (e) => {
const { name, checked } = e.target;
const item = DATA.find(({ payerName }) => payerName === name);
if (checked) {
if (!selected.includes(item)) { // probably not needed
setSelected([...selected, item]);
}
} else {
setSelected(selected.filter((s) => s === item);
}
}

× TypeError: Cannot set property 'name' of undefined when I change data in input field

I am trying to create a React app. It has a button "Add", once you click it, it adds a couple of input fields. Every line of inputs has a button "X" to remove the line from screen.
The issue, I want to fix, happens after removing all lines and adding a new one, when I change data into the input. It throws an error: "TypeError: Cannot set property 'name' of undefined". Please any advices to fix the issue. Thanks.
handleChange
src/App.js:10
7 | let index = event.target.id;
8 | if (event.target.name === "name") {
9 | let newArr = [...datas];
> 10 | newArr[index].name = event.target.value;
| ^ 11 | setDatas(newArr);
12 | }
13 | if (event.target.name === "gender") {
View compiled
TypeError: Cannot set property 'name' of undefined
My code is below:
import { useState } from "react";
const App = () => {
const [datas, setDatas] = useState([]);
const [counter, setCounter] = useState(0);
const handleChange = (event) => {
let index = event.target.id;
if (event.target.name === "name") {
let newArr = [...datas];
newArr[index].name = event.target.value;
setDatas(newArr);
}
if (event.target.name === "gender") {
let newArr = [...datas];
newArr[index].gender = event.target.value;
setDatas(newArr);
}
};
const removeItem = (id) => {
let newItems = datas.filter((product) => product.id !== id);
setDatas(newItems);
};
console.log(datas);
return (
<>
{datas.map((data, index) => {
return (
<li key={index}>
<input
name="name"
id={data.id}
value={data.name}
onChange={handleChange}
/>
<input
name="gender"
id={data.id}
value={data.gender}
onChange={handleChange}
/>
<button onClick={() => removeItem(data.id)}>X</button>
</li>
);
})}
<button
onClick={() => {
setCounter((prevState) => prevState + 1);
setDatas([...datas, { id: counter, name: "", gender: "" }]);
}}
>
Add
</button>
</>
);
};
export default App;
You need the ID of the element to be the index you're iterating over in order to assign to newArr[index] - currently, the ID is the data.id instead, which is presumably not the index.
You're also mutating the state at the moment - create a copy instead of mutating.
{datas.map((data, index) => {
return (
<li key={index}>
<input
name="name"
id={index}
value={data.name}
onChange={handleChange}
/>
And avoid mutation with:
const handleChange = (event) => {
const index = event.target.id;
if (event.target.name === "name") {
let newArr = [...datas];
newArr[index] = ({ ...newArr[index], name: event.target.value });
setDatas(newArr);
}
and use the same pattern for the gender as well.
Another option, to avoid having dynamic IDs (which is a code smell) is to curry the index into the handler first.
const handleChange = index => event => {
and then change
onChange={handleChange}
to
onChange={handleChange(index)}

Simulate "shift" pressing key on checkbox to select multiple rows

I have the following input
<input type="checkbox" checked={isChecked}
onChange={handleOnChange}/>
and my function is this
const handleOnChange = () => {
let element:any = document.querySelector('input');
element.onkeydown = (e: { key: any; }) => alert(e.key);
element.dispatchEvent(new KeyboardEvent('keydown',{'key':'Shift'}));
setIsChecked(!isChecked);
};
This checkbox is created dinamically as I add new rows and I would like to simulate holding the key "shift" so that when I check multiple checkboxes these rows remain selected.
I am using reactjs.
There's no native way to do it but you can implement it based on the index you have checked.
const checkboxes = new Array(20).fill(null);
export default function App() {
const [checked, setChecked] = useState([]);
const lastChecked = useRef(null);
const handleChange = useCallback((e) => {
const index = Number(e.target.dataset.index);
if (lastChecked.current !== null && e.nativeEvent.shiftKey) {
setChecked((prev) => {
const start = Math.min(lastChecked.current, index);
const end = Math.max(lastChecked.current, index);
return uniq([...prev, ...range(start, end), end]);
});
return;
}
if (e.target.checked) {
lastChecked.current = index;
setChecked((prev) => [...prev, index]);
} else {
lastChecked.current = null;
setChecked((prev) => prev.filter((i) => i !== index));
}
}, []);
return (
<div>
{checkboxes.map((_, i) => (
<div key={i}>
<label>
<input
checked={checked.includes(i)}
data-index={i}
type="checkbox"
onChange={handleChange}
/>
checkbox {i}
</label>
</div>
))}
</div>
);
}

Categories