Filter Checkbox issue in react js - reactjs

This is my code for the category filter. I used this in all places.
{categapi.map((data, index) => (
<Form.Check
onChange={props.handleSingleCheck }
key={index}
name={data.category_id}
label={data.category_name}
type="checkbox"
id={data.category_name}
/>
))}
It's the function for the handle the checkbox.
const handleSingleCheck = (e) => {
if (e.target.checked == true) {
const { name } = e.target;
isChecked.push(name);
console.log(isChecked);
setIsChecked([...isChecked]);
} else {
let index = isChecked.indexOf(e.target.name);
isChecked.splice(index, 1);
}
catfilter();
};
when selecting two categories it is selected. But when I click the more button it opens the modal but the selected items are disabled in the modal.
When clicking on the checkbox it needs to check both inside the modal and outside the modal. How to do this?

In your handleSingleCheck function you are pushing the values and slicing the state variable isChecked directly. Which is the wrong way to interact with react state. Learn more here (you should treat the state variables as if they were immutable)
Change your handleSingleCheck logic as follows:
const handleSingleCheck = (e) => {
let isCheckedCopy = [...isChecked] // create a local variable
if (e.target.checked == true) {
const { name } = e.target;
isCheckedCopy.push(name) // push the data into local mutable array
} else {
let index = isCheckedCopy.indexOf(e.target.name);
isCheckedCopy.splice(index, 1); // slice the mutable array
}
console.log(isCheckedCopy);
setIsChecked([...isCheckedCopy]); // change isChecked state through setIsChecked(...)
catfilter();
};

Related

React component not re-rendering on component state change

I am working on a sidebar using a recursive function to populate a nested list of navigation items.
Functionally, everything works except for the re-render when I click on one of the list items to toggle the visibility of the child list.
Now, when I expand or collapse the sidebar (the parent component with its visibility managed in its own state), the list items then re-render as they should. This shows me the state is being updated.
I have a feeling this possibly has something to do with the recursive function?
import React, { useState } from "react";
import styles from "./SidebarList.module.css";
function SidebarList(props) {
const { data } = props;
const [visible, setVisible] = useState([]);
const toggleVisibility = (e) => {
let value = e.target.innerHTML;
if (visible.includes(value)) {
setVisible((prev) => {
let index = prev.indexOf(value);
let newArray = prev;
newArray.splice(index, 1);
return newArray;
});
} else {
setVisible((prev) => {
let newArray = prev;
newArray.push(value);
return newArray;
});
}
};
const hasChildren = (item) => {
return Array.isArray(item.techniques) && item.techniques.length > 0;
};
const populateList = (data) => {
return data.map((object) => {
return (
<>
<li
key={object.name}
onClick={(e) => toggleVisibility(e)}
>
{object.name}
</li>
{visible.includes(object.name) ? (
<ul id={object.name}>
{hasChildren(object) && populateList(object.techniques)}
</ul>
) : null}
</>
);
});
};
let list = populateList(data);
return <ul>{list}</ul>;
}
export default SidebarList;
There are many anti patterns with this code but I will just focus on rendering issue. Arrays hold order. Your state does not need to be ordered so it's easier to modify it, for the case of demo I will use object. Your toggle method gets event, but you want to get DOM value. That's not necessary, you could just sent your's data unique key.
See this demo as it fixes the issues I mentioned above.

Remove item from String Array ReactJS

I'm working with ReactJS with Typescript and the goal is to have a checkbox component that adds and removes items to a string array according to whether the checkbox is selected or not. My current function only adds to the array, so when I select then unselect a checkbox the item is added twice. Thanks in advance.
Function:
const handleGroupChange = (groupOptions: any) => {
const existSelection = selectedGroups;
existSelection.push(groupOptions.target.value);
setSelectedGroups(existSelection);
}
};
Checkbox:
<FormControlLabel
control={
<Checkbox
color="primary"
onChange={e => handleGroupChange(e)}
value={"MATCHED_MENTORS"}
/>
}
check whether checkbox is checked or not on the onChange event.
const handleGroupChange = (groupOptions: any) => {
const existSelection = selectedGroups;
if (groupOptions.target.checked) {
existSelection.push(groupOptions.target.value);
} else {
var index = existSelection.indexOf(groupOptions.target.value);
if (index !== -1) {
existSelection.splice(index, 1);
}
}
setSelectedGroups(existSelection);
}
You need to check the checkbox boolean property whether it is checked or not. Please follow this example below and here is the Codesandbox
const [selectedGroups, setSelectedGroups] = useState([]);
const handleGroupChange = (groupOptions) => {
let existSelection = selectedGroups;
if (groupOptions.target.checked)
existSelection.push(groupOptions.target.value);
else
existSelection = existSelection.filter(item => item !== groupOptions.target.value)
console.log(selectedGroups);
setSelectedGroups(existSelection);
The thing here is that you are pushing a boolean as item therefore the existSelection array should look something like [true, false, true, false] more or less. That should be something to take a look at if it is the behavior you want.
If I understood corretcly what you want is to practice, maybe you can try this:
const randomStrings = ['gelato', 'frozen_yogurt', 'ice_cream'];
and then when you click on the Checkbox it will trigger onChange wich will be connected to handleGroupChange function and this would look something like:
const handleGroupChange = (isChecked) = {
if(isChecked){
//We create a random number between 0 and 2
const randomIndex = Math.floor(Math.random() * 2);
//We select an element from randomStrings array based on the random
index generated above
const randomStr = this.randomStrings[randomIndex];
//Looks if the randomStr already exist in existSelection
if nothing found it findIndex returns - 1 wich tell us
this item is not in the array so we push it.
const existInArr = this.existSelection.findIndex(randomStr);
if(existInArr === - 1){
existSelection.push(randomStr);
}
//I guess you also wanna feed a useState hook here.
setExistSelection(randomStr);
}
};
This should do the trick.

how do we push a checkbox to an array while checkbox is by default checked in react js?

the checkbox by default checked if this checkbox is by default checked then it should be automatically pushed into addtess_type array.
https://react-jkrrcb.stackblitz.io demo
handleInputChangeDom = (event) => {
const target = event.target;
var value = target.value;
const Address_type = this.state.Address_type;
if (event.target.checked) {
Address_type.push(event.target.value);
} else {
let index = Address_type.indexOf(event.target.value);
Address_type.splice(index, 1);
}
this.setState({
Address_type: Address_type,
});
};
I think you are trying to maintain a state of all the checked items.
One advice would be to use Set instead of an array. It will make life so much more easier for you.
const handleInputChangeDom = (event) => {
const newSet = new Set(this.state.Address_type); // create a clone, don't update current state
if(event.target.checked) {
newSet.add(event.target.value);
} else {
newSet.delete(event.target.value);
}
this.setState({ Address_type: newSet });
}
One suggestion - please try to use camelCase with React, that is suggested.

Filtering an icon from an array of icon strings for re-render

I'm trying to take an e.target.value which is an icon and filter it out from an array in state, and re-render the new state minus the matching icons. I can't seem to stringify it to make a match. I tried pushing to an array and toString(). CodeSandbox
✈ ["✈", "♘", "✈", "♫", "♫", "☆", "♘", "☆"]
Here is the code snippet (Parent)
removeMatches(icon) {
const item = icon;
const iconsArray = this.props.cardTypes;
const newIconsArray =iconsArray.filter(function(item) {
item !== icon
})
this.setState({ cardTypes: newIconsArray });
}
This is a function in the parent component Cards, when the child component is clicked I pass a value into an onClick. Below is a click handler in the Child component
handleVis(e) {
const item = e.target.value
this.props.removeMatches(item)
}
First of all, there's nothing really different about filtering an "icon" string array from any other strings. Your example works like this:
const icons = ["✈", "♘", "✈", "♫", "♫", "☆", "♘", "☆"]
const icon = "✈";
const filteredIcons = icons.filter(i => i !== icon);
filteredIcons // ["♘", "♫", "♫", "☆", "♘", "☆"]
Your CodeSandbox example has some other issues, though:
Your Card.js component invokes this.props.removeMatches([item]) but the removeMatches function treats the argument like a single item, not an array.
Your Cards.js removeMatches() function filters this.props.cardTypes (with the previously mentioned error about treating the argument as a single item not an array) but does not assign the result to anything. Array.filter() returns a new array, it does not modify the original array.
Your Cards.js is rendering <Card> components from props.cardTypes, this means that Cards.js is only rendering the cards from the props it is given, so it cannot filter that prop from inside the component. You have a few options:
Pass the removeMatches higher up to where the cards are stored in state, in Game.js as this.state.currentCards, and filter it in Game.js which will pass the filtered currentCards back down to Cards.js.
// Game.js
removeMatches = (items) => {
this.setState(prevState => ({
currentCards: prevState.currentCards.filter(card => items.indexOf(card) == -1)
}));
}
// ...
<Cards cardTypes={this.state.currentCards} removeMatches={this.removeMatches} />
// Cards.js
<Card removeMatches={this.props.removeMatches}/>
// Card.js -- same as it is now
Move Cards.js props.cardTypes into state (ex state.currentCards) within Cards.js, then you can filter it out in Cards.js and render from state.currentCards instead of props.cardTypes. To do this you would also need to hook into componentWillReceiveProps() to make sure that when the currentCards are passed in as prop.cardTypes from Game.js that you update state.currentCards in Cards.js. That kind of keeping state in sync with props can get messy and hard to follow, so option 1 is probably better.
// Cards.js
state = { currentCards: [] }
componentWillReceiveProps(nextProps) {
if (this.props.cardTypes !== nextProps.cardTypes) {
this.setState({ currentCards: nextProps.cardTypes });
}
}
removeMatches = (items) => {
this.setState(prevState => ({
currentCards: prevState.currentCards.filter(card => items.indexOf(card) == -1)
}));
}
render() {
return (
<div>
{ this.state.currentCards.map(card => {
// return rendered card
}) }
</div>
);
}
Store all the removed cards in state in Cards.js and filter cardTypes against removedCards before you render them (you will also need to reset removedCards from componentWillReceiveProps whenever the current cards are changed):
// Cards.js
state = { removedCards: [] }
componentWillReceiveProps(nextProps) {
if (this.props.cardTypes !== nextProps.cardTypes) {
this.setState({ removedCards: [] });
}
}
removeMatches = (items) => {
this.setState(prevState => ({
removedCards: [...prevState.removedCards, ...items]
}));
}
render() {
const remainingCards = this.props.cardTypes.filter(card => {
return this.state.removedCards.indexOf(card) < 0;
});
return (
<div>
{ remainingCards.map(card => {
// return rendered card
})}
</div>
);
}
As you can see, keeping state in one place in Game.js is probably your cleanest solution.
You can see all 3 examples in this forked CodeSandbox (the second 2 solutions are commented out): https://codesandbox.io/s/6yo42623p3

REACT.js initial radio select

I have been trying to modify existing code by adding a few of new functionalities.
I have this function that renders set of radiobuttons based on variable ACCREDITATION_TYPES:
createRadioCalibration(name) {
const { id, value, labels } = this.props;
const _t = this.props.intl.formatMessage;
const ACCREDITATION_TYPES = [
[CALIBRATION_ACCREDITED, _t(messages.calibrationAccredited)],
[CALIBRATION_NOT_ACCREDITED, _t(messages.calibrationNotAccredited)]
];
return <FormChoiceGroup
type = "radio"
values = {ACCREDITATION_TYPES.map(mapValueArray)}
key = {`${id}_${name}`}
name = {`${id}_${name}`}
value = {value[name]}
handleChange = {this.handleFieldChangeFn(name)}
/>;
}
The radios by default are all unchecked. When clicked this function is fired up:
handleFieldChangeFn(name) {
return (e) => {
const { handleFieldChange, id } = this.props;
handleFieldChange(id, name, e.target.value);
};
}
The form is rendered as follows:
render () {
const FIELDS = {
[CALIBRATION]: this.createRadioCalibration(CALIBRATION),
};
return (
<div className="">
<label>{_t(messages.calibration)}</label>
{ FIELDS[CALIBRATION] }
How can I select any option I want as an initial state? There are only two options now but what if there were five?
Also how can I manipulate the radio selection based on onChange event of other elements of the form, namely a drop down menu?
Answering my question I got two minuses for asking: in the constructor one needs to add the following this.props.handleFieldChange( 'id', 'radio_button_group_name', 'value_of_the_output_that_should_be_selected' );
That will select particular radio in a loop.

Resources