handling a select all in react and materialUI - javascript

I have several sets of checkboxes that should work independently and a toggle that toggles them all (within their groups only). I have two states (one for the toggle and one for the checkboxes). I can identify the checkbox I'm clicking with the event.target.value, that way I can manipulate it individually. But I'm having trouble controlling them all at once with the toggle as well as making the toggle come active when someone independently checks them all true.
In summary
1- when the toggle is on, all checkboxes within its group come on, same for off
2- When I turn on each checkbox individually until they are all on, the toggle turns on and when I uncheck one of them, the toggle turns off
I've made a sandbox for you to play on. Thanks in advance
const [active, setActive] = useState(false)
const [singleactive, setSingleActive] = useState([])
const handleSwitch = (e) => {
if(e.target.value === "Select All") {
setActive(e.target.checked)
setSingleActive([...singleactive])
} else {
setSingleActive([])
}
}
const handleSingleSwitch = (e) => {
const index = singleactive.indexOf(e.target.value)
if(index === -1) {
setSingleActive([...singleactive, e.target.value])
} else {
setSingleActive(singleactive.filter((singleactive) => singleactive !== e.target.value))
}
}

Well, I figure it out. Though I find answering your own question a bit pretentious lol, here it is in case it helps
Updated states and functions
const [active, setActive] = useState(false)
/// fill a new array with false values for the length of the data to load them all unchecked
const [checkedState, setCheckedState] = useState(new Array(data.length).fill(false));
const handleSwitch = (e) => {
if(e.target.checked) {
/// if the toggle event comes back checked, set the active state to true and re-fill the array with all true values which is what Select All does
setActive(true)
setCheckedState(new Array(data.length).fill(true));
} else {
/// if the toggle event comes back unchecked, set the active state to false and re-fill the array with all false values which is what Deselect All does
setActive(false)
setCheckedState(new Array(data.length).fill(false));
}
}
const handleOnChange = (position) => {
// every time you click an infividual checkbox, map through the state and compare its index with the position in the array. If it's true, make it true otherwise false; then set the state with this value
const updatedCheckedState = checkedState.map((item, index) => {
return (
index === position ? !item : item
)}
);
setCheckedState(updatedCheckedState);
/// if the new generated array of values contains at least one false in it, set the active class on the toggle to false, but if there isn't at least one false, then all are true, so set the active class to true on the toggle
if(updatedCheckedState.includes(false)) {
setActive(false)
} else {
setActive(true)
}
};
I also removed the value on the toggleAll checkbox that I inadvertently set statically to Select All. This way I can control it via state
<Stack direction="row" spacing={1} alignItems="center">
<Typography>Deselect All</Typography>
<Switch
size="small"
checked={active}
onChange={handleSwitch} />
<Typography>Select All</Typography>
</Stack>
And lastly the checkboxes
<FormControlLabel
control={
<Checkbox
size="small"
name={item.toLowerCase()}
value={item.toLowerCase()}
checked={checkedState[index]}
onChange={() => handleOnChange(index)}
/>
}
label= {item.replaceAll('_', ' ')} />
Check the sandbox for the updated code.

Related

Get a checkbox value on change of other checkbox in react

I have 2 checkboxes A and B. I want to check whether checkbox B is checked or not on change of checkbox A and vice versa. Also I want to change checkbox B on change of checkbox A. How can we achieve that in react.js.
You can create a state for both of them and change it accordingly.
This way you'll have access to it whenever needed.
Also, to avoid handling changes separately for every input, you can give each of them a name and then have a single dedicated function that changes the value of the checkbox based on it's name.
Example:
function App() {
const [state, setState] = useState({
firstCheckbox: false,
secondCheckbox: false,
})
const handleChange = (e) => {
setState(prev => ({
...prev,
[e.currentTarget.name]: e.currentTarget.checked,
}));
};
return (
<>
<input
name='firstCheckbox'
type='checkbox'
checked={state.firstCheckbox}
onChange={handleChange}
/>
<input
name='secondCheckbox'
type='checkbox'
checked={state.secondCheckbox}
onChange={handleChange}
/>
</>
)
}
Currently in this example, each checkbox relates to it's own state.
However, you can easily adjust the handleChange function based on your needs.

Add animation to filter items on click

I created a filter gallery. I want to animate the filter items every time I click to a buttons. But my codes are not doing it properly. It animates filter items like toggle. If I click on a button first time it animates items, then If I click on another button it shows nothing. After that If I click on another button it animates again. What's wrong with my code? Experts please help me to find out the proper solution. Thanks in advance.
Here is my code:
import React, { useState } from 'react';
import suggestData from '../data/suggest-data.json';
const allCategories = ['All', ...new Set(suggestData.map(item => item.area))];
const Suggest = () => {
const [suggestItem, setSuggestItem] = useState(suggestData);
const [butto, setButto] = useState(allCategories);
const [selectedIndex, setSelectedIndex] = useState(0);
const [anim, setAnim] = useState(false);
const filter = (button) => {
if (button === 'All') {
setSuggestItem(suggestData);
return;
}
const filteredData = suggestData.filter(item => item.area === button);
setSuggestItem(filteredData);
}
const handleAnim = () => {
setAnim(anim => !anim);
}
return (
<div>
<h1>Suggest</h1>
<div className="fil">
<div className="fil-btns">
<div className="fil-btn">
<button className='btn'>Hello</button>
{
butto.map((cat, index) => {
return <button type="button" key={index} onClick={() => { filter(cat); setSelectedIndex(index); handleAnim(); }} className={"btn" + (selectedIndex === index ? " btn-active" : "")}>{cat}</button>
})
}
</div>
</div>
<div className="fil-items">
{
suggestItem.map((item, index) => {
return (
<div className={"fil-item" + (anim ? " fil-item-active" : "")} key={index}>
<h1>{item.name}</h1>
<h2>{item.category}</h2>
<h3>{item.location}</h3>
<h4>{item.type}</h4>
<h5>{item.area}</h5>
</div>
);
})
}
</div>
</div>
</div>
);
}
export default Suggest;
In your handleAnim() function, you are simply toggling the value of anim state. So initially, its value is false and when you click the button for the first time, it is set to true. On clicking the next button, the anim state becomes false because the value of !true is false and hence your animation doesn't work. On next click, becomes true again since !false is true and the toggle continues again and again.
If you want to make your animations work on every click you will need to set the anim state value to true on every button click as below since you seem to depend on the value to set animations. As an alternative, I think it will do just fine if you simply add the animation directly to the enclosing div with class .filter-item instead of relying on states to trigger the animation since after every filter you apply, the elements will be re-rendered and the animation will happen after every re-render.
const handleAnim = () => {
setAnim(true);
}

Work out whether custom radio component is checked or not

I have a custom radio component in React, when I check and uncheck the values it adds items to an object and should have true or false based on whether they are checked.
At the moment it adds the true value correctly with the name of the radio but I can't seem to find out how to work to make the option false if another option is chosen.
I am currently using
constructor() {
super();
this.state = {
time_frame: {},
}
this.handleRadioChange = this.handleRadioChange.bind(this);
}
handleRadioChange(event) {
let name = event.target.name
let timeFrameCopy = this.state.time_frame;
console.log(event.target)
timeFrameCopy[event.target.value] = true
this.setState({[name]: timeFrameCopy,}, this.checkState)
return
}
}
checkState(event) {
console.log(this.state)
}
My radio component is
const Radio = (props) => {
return (
<Col>
<div>
<input id={props.value} type="radio" name={props.name} value={props.value} className="visually-hidden" onChange={props.handleChange}/>
<label htmlFor={props.value} className="switch-label checkbox-label text-center">{props.label}</label>
</div>
</Col>
)
}
export default Radio
If I check one radio button and then the other my state still has the data:
time_frame: {single: true, recurring: true}
Even though I would expect one of them to be false
If I understand correctly, you're trying to store in the state an object called time_frame, which is going to contain one pair of property-value per radio input, where the name of each of them would be the property name and the checked status the value. If that's the case, I see a logic problem. since you're hard-coding true (for what I understand from your code) always instead of looking for the value stored and toggling/flipping it.
handleRadioChange() function should be something like:
handleRadioChange(event) {
let name = event.target.name;
this.setState((currentState)=>{
let timeFrameCopy = currentState.time_frame;
timeFrameCopy[name] = event.target.checked;
return { "time_frame": timeFrameCopy };
});
}

React - change state of one button from 100 buttons array, and reset state of all buttons on one click

So, I have 100 mapped buttons from array, something like that
{
buttons.map((button, index) =>
<StyledGameButton
key={index}
value={button}
onClick={this.checkButton}>
{ button < 10 ? `0${button}` : button }
</StyledGameButton>
)
}
I want user to click all the buttons from 0 to 99, so when he click for example 0 the button should change color. I made function checking if he clicked correct button, and if yes then I am addind data-attr to that button (thats how I am changing the color of buttons):
checkButton = e => {
const buttonId = e.currentTarget.getAttribute('value');
const nextButton = this.state.currentButton === null ? 0 : this.state.currentButton + 1;
if (buttonId == nextButton){
this.setState({
currentButton: parseInt(buttonId),
});
if (this.state.currentButton === 99) {
this.gameEnd();
};
e.currentTarget.setAttribute('data-status', 'correct');
}
}
The problem is that I want to make reset button, that will change all buttons to 'unclicked' so I have to delete data-attr from all buttons on one click. How can I do this? Or is there a better solution to manage 'state' of single button without making 100 states?
100 checkboxes demo
use an Array to store the state would be fine.
const list = [...Array(100).keys()].map(x => ({ id: x }));
const App = () => {
const [selected, setSelected] = React.useState([]); // init with empty list
const onChangeHandler = id => () => { // pass index/identify params
selected.includes(id) // check whether been checked
? setSelected(selected.filter(x => x !== id)) // yes, remove
: setSelected([...selected, id]); // no, add
};
const onRemove = () => {
setSelected([]); // remove all, set to empty list
};
return (
<div className="App">
{list.map(item => (
<input
type="checkbox"
key={item.id}
checked={selected.includes(item.id)}
onChange={onChangeHandler(item.id)}
/>
))}
<br />
<button onClick={onRemove}>Remove all</button>
<div>{selected.join(",")}</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

FlatList item doesn't update when I setState()

I'm building multiple select modal. When user press the item, the item should be marked as 'Checked'.
Problem I added/removed id from id arrays. When I open and check modal, it doesn't show 'Check' sign. But when I close and open the modal again, it shows 'Check' Sign.
To keep track of selected items, I defined the items in the modal component's state.
state = {
selectedSeasonIds: this.props.selectedSeasonIds,
}
Here is react-native-modal which I use to show modal on the screen
<Modal
isVisible={isSelectorVisible}
onBackdropPress = {() => this.props.hideSelector()}>
<View style={styles.modalContainer}>
<FlatList
style={styles.root}
data={this.props.items}
ItemSeparatorComponent={this._renderSeparator}
keyExtractor={this._keyExtractor}
renderItem={this._renderItemForMultiple}/>
</View>
</Modal>
This is render function for each item
_renderItemForMultiple = ({item}) => {
return (
<TouchableOpacity
style={styles.itemStyle}
onPress={() => {this._handleMultipleItemPress(item.id)}}>
<RkText>{item.value}</RkText>
{ this._renderCheck(item.id) } <<< Here is the problem
</TouchableOpacity>
);
}
When user clicks the item, FlatList's item calls _handleMultipleitemPress
_handleMultipleItemPress = (id) => {
let { selectionType } = this.props;
let { selectedSeasonIds, selectedSizeIds, selectedColorIds } = this.state;
if(selectionType===2) {
if(_.includes(this.state.selectedSeasonIds, id)) {
let newSelectedSeasonIds = _.filter(this.state.selectedSeasonIds, (curObject) => {
return curObject !== id;
});
this.setState({selectedSeasonIds : newSelectedSeasonIds});
} else {
let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
this.setState({selectedSeasonIds : newSelectedSeasonIds});
}
}
// season Select Action
this.props.seasonSelectAction(id);
}
Problem We added/removed id from id arrays. When I open and check modal, it doesn't show 'Check' sign. But when I close and open the modal again, it shows 'Check' Sign.
Somehow the modal is not rendered even eventhough we setState in renderCheck(). Why is it happening? And How can I fix it?
_renderCheck = (id) => {
let { selectionType, selectedSeasonIds, selectedSizeIds, selectedColorIds } = this.props;
if(selectionType===2) {
if(_.includes(this.state.selectedSeasonIds, id)) {
return (<RkText>Check </RkText>);
}
}
return (<RkText> </RkText>);
}
Any other advice will be also appreciated! Thanks for reading this post.
UPDATE I debugged with code and when I press the item, it doesn't go through _renderItemForMultiple. I think it's because I didn't define a param for _renderItemForMultiple. How can I pass item to its param? Any idea?
Even though your state changes, you're not passing it to <FlatList>, so its props don't change. Its shouldComponentUpdate method returns false when none its props change. As the docs state:
By passing extraData={this.state} to FlatList we make sure FlatList itself will re-render when the state.selected changes. Without setting this prop, FlatList would not know it needs to re-render any items because it is also a PureComponent and the prop comparison will not show any changes.
So you need to pass extraData={this.state} to FlatList.

Categories