I want to be able to increment and decrement values using a single onChange and handleIncrement in a mapped functional component.
when the click function runs it updates all the listed inputs with the same value.
please, how can i get them to update each individually?
I am still quite the noob.
here is my code:
const [state, setState] = useState({
count: 0,
});
const handleChange = (e) => {
setState({
...state.count,
[e.target.name]: e.target.value,
});
};
const handleIncrement = () => {
setState((prevState) => {
return { count: prevState.count + 1 };
});
};
const listings = Consumables.map((list) => (
<Col lg={4}>
<Bounce up>
<Card
key={list.product}
style={{
width: "100%",
height: "25rem",
marginTop: "5%",
textAlign: "center",
}}
>
<Card.Header as="h3">{list.product}</Card.Header>
<Card.Body>
{list.productImage}
<br />
<br />
{list.description}
<br />
<br />
{list.weight}
<h5>Price: {list.price}</h5>
</Card.Body>
<Card.Footer>
<InputGroup className="mb-3">
<InputGroup.Prepend>
<Button variant="danger">-</Button>
</InputGroup.Prepend>
<FormControl
name={list.product}
type="text"
value={state.count}
onChange={handleChange}
/>
<InputGroup.Append>
<Button variant="success" onClick={handleIncrement}>
+
</Button>
</InputGroup.Append>
</InputGroup>
</Card.Footer>
</Card>
</Bounce>
</Col>
));
You just need to get the index of your counter to be able to increment it individually :
import React, { Component } from "react";
import { render } from "react-dom";
const App = () => {
const [counters, setCounters] = React.useState([0, 0, 0, 0]);
const setCount = (index, newCounter) => {
const newCounters = Object.assign([], counters);
newCounters[index] = newCounter;
setCounters(newCounters);
};
return (
<div>
{counters.map((counter, index) => (
<Counter index={index} counter={counter} setCount={setCount} />
))}
</div>
);
};
const Counter = ({ index, counter, setCount }) => {
return (
<div>
Counter {index} = {counter}{" "}
<button onClick={() => setCount(index, counter + 1)}>Increment</button>
</div>
);
};
render(<App />, document.getElementById("root"));
I get the index of the mapped counter, and I give it to the component so I can easily point to the correct counter in the state. The update is done with just one method that take this index as parameter.
Here is the repro on stackblitz
Related
Currently I have a map function that render a serial of image, and I realized that they share the same hover state, which means they will perform the same action when hovered. Is there are any standard practice to map duplicate components while assigning them unique/individual properies?
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={item.img}
alt={item.title}
loading="lazy"
onMouseOver={() => {setHover(true)}}
onMouseOut={() => {setHover(false)}}
style={{ transform: hover ? 'scale(1.5, 1.5)' : null }}
/>
<ImageListItemBar
title={item.title}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title}`}
>
<InfoIcon />
</IconButton>
}
/>
You should use a component, which create a unique state for each element, i wrote an easy to understand example.
import React, { useState } from "react"
const items = [
{
title: 'Card1',
price: 100
},
{
title: 'Card2',
price: 50
},
{
title: 'Card3',
price: 200
},
]
export default function App() {
return (
<>
{
items.map(element => {
return(
<Card {...element}/>
)
})
}
</>
)
}
function Card({title, price, key}) {
const [isHovered, setHover] = useState(false)
return (
<>
<div
key={key}
onMouseOver={() => {setHover(true)}}
onMouseOut={() => {setHover(false)}}
>
<div>
{title}
</div>
<h3>
{
isHovered && price
}
</h3>
</div>
</>
);
}
I made the card price to show if hovered so you can see it works on each individual component.
Code sandbox if you want to check it out.
To provide unique properties, you need to have something that uniquely identifies your image component and use it to manage your state. In your case, your state hover should be an array or an object, not a boolean. Since you are using item.img as a key, I assume it is unique and hence it can help in your state management like this:
const [hover, setHover] = useState({});
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={item.img}
alt={item.title}
loading="lazy"
onMouseOver={() => setHover({...hover, [item.img]: true})}
onMouseOut={() => setHover({...hover, [item.img]: false})}
style={{ transform: hover ? 'scale(1.5, 1.5)' : null }}
/>
<ImageListItemBar
title={item.title}
subtitle={item.author}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info about ${item.title}`}
>
<InfoIcon />
</IconButton>
}
/>
))
}
If you want the state to be in the parent without going all the way to an array or object, you can use a number instead. If only one item at a time is going to be active, you can just use the index of the active item as the state:
const { useState } = React;
const things = ["foo", "bar", "baz"];
function Component() {
const [active, setActive] = useState(-1);
const updateActivity = (index) => setActive(index === active ? -1 : index);
return (
<ul>
{things.map((thing, index) => (
<li>
<button key={index} onClick={() => updateActivity(index)}>
{index === active
? <strong>{thing}</strong>
: thing}
</button>
</li>
))}
<li>Value: {active}</li>
</ul>
);
}
ReactDOM.render(
<Component />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Alternatively, in cases where you want multiple items to be simultaneously active, you can use a "bit flag" approach where each bit of the value represents whether or not the corresponding index is active:
const { useState } = React;
const things = ["foo", "bar", "baz"];
function Component() {
const [active, setActive] = useState(0);
const updateActivity = (index) => setActive(active ^ Math.pow(2, index));
return (
<ul>
{things.map((thing, index) => (
<li>
<button key={index} onClick={() => updateActivity(index)}>
{active & Math.pow(2, index)
? <strong>{thing}</strong>
: thing}
</button>
</li>
))}
<li>Value: {active} ({active.toString(2).padStart(3, "0")})</li>
</ul>
);
}
ReactDOM.render(
<Component />,
document.getElementById("react2")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js"></script>
<div id="react2"></div>
I am working on a react where I am dealing with dynamic number of input fields as I am rendering them using arr.map function. But How can I handle the input onChange method with so many input fields?
Here's my component:
this.props.setsList.map((codeset, index) => (
<Table.Row key={codeset.code_system_id}>
<Table.Cell>{index + 1}</Table.Cell>
<Table.Cell>{codeset.name}</Table.Cell>
<Table.Cell>
<Input
type='text'
className='form-control'
value={codeset.code}
placeholder={translateText('Code')}
onChange={() => this.handleCodeChange(event, index)}
style={{ height: '70%' }}
/>
</Table.Cell>
<Table.Cell>
<Input
type='text'
className='form-control'
value={codeset.description}
placeholder={translateText('Code Description')}
onChange={() => this.handleCodeDescriptionChange(event, index)}
style={{ height: '70%' }}
/>
</Table.Cell>
</Table.Row>
All the input fields may have existing fields or may be empty and one can edit it them semd the edit fields to the API. How can I handle such a case with just 1 handleFunction. Is there a way? Any leads will be appreciated.
In the above code, 2 input boxes are there with initial values from the api.
Data passed into your component should go directly into state. Then each field sends the array index, field name and new value to the handleChange callback.
import { useState } from "react";
// passed in as a prop from the parent component
const data = [
{
name: "foo",
code: "gdfgsd"
},
{
name: "bar",
code: "gfdsgsdfgfd"
}
];
const App = ({ setsList = data }) => {
const [state, setState] = useState(setsList);
const handleChange = (e, i) => {
const { value, name } = e.target;
const newState = [...state];
newState[i] = {
...newState[i],
[name]: value
};
console.log(newState);
setState(newState);
};
return (
<div className="App">
{state.map(({ name, code }, index) => {
return (
<div key={index}>
<label>
name
{": "}
<input
name="name"
value={name}
onChange={(e) => handleChange(e, index)}
/>
</label>
<label>
code
{": "}
<input
name="code"
value={code}
onChange={(e) => handleChange(e, index)}
/>
</label>
</div>
);
})}
</div>
);
};
export default App;
There's also a way that you can render each field dynamically by mapping over the array, and then inner mapping over that array item's keys.
import { useState } from "react";
import { data } from "./data";
const App = ({ setsList = data }) => {
const [state, setState] = useState(setsList);
const handleChange = (e, i) => {
const { value, name } = e.target;
const newState = [...state];
newState[i] = {
...newState[i],
[name]: value
};
console.log(newState);
setState(newState);
};
return (
<table className="App">
<tbody>
{state.map((item, index) => (
<tr key={index}>
{Object.keys(item).map((key) => (
<td key={`${index}-${key}`}>
<label>
{key}
{": "}
<input
name={key}
value={item[key]}
onChange={(e) => handleChange(e, index)}
/>
</label>
</td>
))}
</tr>
))}
</tbody>
</table>
);
};
export default App;
Here is my code
const useStyles = makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1)
},
},
button: {
margin: theme.spacing(1)
}
}))
function CreateCourse() {
const classes = useStyles();
const [sectionFields, setSectionFields] = useState([{
sectionName: '',
overview: '',
videoContents: [{
videoName: '', videoUrl: ''
}]
}])
function handleChangInput(index, event) {
const values = [...sectionFields];
values[index][event.target.name] = event.target.value;
setSectionFields(values);
}
function handleChangVideoInput(index, i, event) {
const values = [...sectionFields];
values[index].videoContents[i][event.target.name] = event.target.value;
setSectionFields(values);
console.log(index, event.target.name)
}
const handleSubmit = (event) => {
event.preventDefault();
console.log("Input Field ", sectionFields)
}
const handleRemoveFields = (index) => {
const values = [...sectionFields];
values.splice(index, 1)
setSectionFields(values)
}
const handleAddFields = () => {
setSectionFields([...sectionFields, {
sectionName: '',
overview: '',
videoContents: [{videoName: '', videoUrl: ''}]
}])
}
return (
<div className='container mb-5'>
<Container>
<h1>Add New Member</h1>
<form className={classes.root} onSubmit={handleSubmit}>
{sectionFields.map((inputField, index) => (
<div key={index}>
<TextField
name="sectionName"
label="Section Name"
variant="filled"
value={inputField?.sectionName}
onChange={event => handleChangInput(index, event)}
/>
<TextField
name="overview"
label="Section Overview"
variant="filled"
value={inputField?.overview}
onChange={event => handleChangInput(index, event)}
/>
<IconButton onClick={() => handleRemoveFields(index)}>
<RemoveIcon/>
</IconButton>
<IconButton onClick={handleAddFields}>
<AddIcon/>
</IconButton>
{inputField?.videoContents?.map((v, i) => (
<div key={i}>
<TextField
name="videoName"
label="Enter Video Name"
variant="filled"
value={v.videoName}
onChange={event => handleChangVideoInput(index, i, event)}
/>
<TextField
name="videoUrl"
label="Enter Video Url"
variant="filled"
value={v.videoUrl}
onChange={event => handleChangVideoInput(index, i, event)}
/>
</div>
))}
</div>
))}
<Button
className={classes.button}
variant='contained'
color='primary'
type='submit'
endIcon={<Icon/>}
onClick={handleSubmit}
>
SEND
</Button>
</form>
</Container>
</div>
);
}
export default CreateCourse;
Output in Screenshot
when i click on plus icon creates a new input like
But I want one sectionName has many videoName and videoUrl like I want to create plus icon on the videoUrl side and when user clicks plus icon, it creates many videoName and videoUrl as many as user wants and if user clicks section then it creates one section row with one video row. How can I solve this using react?
First of all, when you use the current value of a state in order to calculate the new state value, it's preferable to use a callback function. This way it's not influenced by re-renders and guarantees the calculation uses the most updated state value.
So assuming you have
const [state, setState] = useState([]);
Don't use:
const next = [...state, newElement];
setState(next);
But instead, use:
setState((previous) => [...previous, newElement]);
In order to add more fields into a nested array, you can update the state like this:
function addToSection(i) {
setSectionFields((prev) => (
const updatedSection = {
...prev[i],
videoContents: [
...prev[i].videoContents,
{ videoName: '', videoUrl: '' },
],
};
return prev.map((section, index) => {
return index === i ? updatedSection : section;
});
);
}
After many hours of trying finally i did it and thanks to #GalAbra , he saves my lot of time and i post this because if it helps to anyone
import React, {useState} from "react";
import Container from "#material-ui/core/Container";
import {TextField} from "#material-ui/core";
import Icon from "#material-ui/icons/Send";
import {makeStyles} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import RemoveIcon from '#material-ui/icons/Delete';
import AddIcon from "#material-ui/icons/Add";
const useStyles = makeStyles((theme) => ({
root: {
'& .MuiTextField-root': {
margin: theme.spacing(1)
},
},
button: {
margin: theme.spacing(1)
}}))
function CreateCourse() {
const classes = useStyles();
const [sectionFields, setSectionFields] = useState([{
sectionName: '',
overview: '',
videoContents: [{
videoName: '', videoUrl: ''
}]}])
function handleChangInput(index, event) {
const values = [...sectionFields];
values[index][event.target.name] = event.target.value;
setSectionFields(values);
}
function handleChangVideoInput(index, i, event) {
const values = [...sectionFields];
values[index].videoContents[i][event.target.name] = event.target.value;
setSectionFields(values);
console.log(index, event.target.name)
}
const handleSubmit = (event) => {
event.preventDefault();
console.log("Input Field ", sectionFields)
}
const handleRemoveFields = (index) => {
const values = [...sectionFields];
if(index > 0) values.splice(index, 1)
setSectionFields(values)
}
const handleRemoveVideoFields = (index, i) => {
const values = [...sectionFields];
if(i > 0)
values[index].videoContents.splice(i, 1)
setSectionFields(values)
}
const handleAddFields = (index) => {
setSectionFields((prevState => (
[...prevState, {
videoContents: [{videoName: '', videoUrl: ''}]
}]
)))
}
const handleAddVideoFields = (i) => {
setSectionFields(prev => {
const updatedSection = {
...prev[i],
videoContents: [
...prev[i].videoContents,
{videoName: '', videoUrl: ''},
],
};
return prev.map((section, index) => {
return index === i ? updatedSection : section;
});
})
}
return (
<div className='container mb-5'>
<Container>
<form className={classes.root} onSubmit={handleSubmit}>
{sectionFields.map((inputField, index) => (
<div key={index}>
<p className='mb-0 mt-3 ml-2'>Enter Section Name</p>
<TextField
name="sectionName"
label="Section Name"
variant="filled"
value={inputField?.sectionName}
onChange={event => handleChangInput(index, event)}
/>
<TextField
name="overview"
label="Section Overview"
variant="filled"
value={inputField?.overview}
onChange={event => handleChangInput(index, event)}
/>
<IconButton onClick={() => handleRemoveFields(index)}>
<RemoveIcon/>
</IconButton>
<IconButton onClick={() => handleAddFields(index)}>
<AddIcon/>
</IconButton>
{inputField?.videoContents?.map((v, i) => (
<div key={i}>
<TextField
name="videoName"
label="Enter Video Name"
variant="filled"
value={v.videoName}
onChange={event => handleChangVideoInput(index, i, event)}
/>
<TextField
name="videoUrl"
label="Enter Video Url"
variant="filled"
value={v.videoUrl}
onChange={event =>
handleChangVideoInput(index, i, event)}
/>
<IconButton onClick={() =>
handleRemoveVideoFields(index, i)}>
<RemoveIcon/>
</IconButton>
<IconButton onClick={() =>
handleAddVideoFields(index)}>
<AddIcon/>
</IconButton>
</div>
))}
</div>
))}
<Button
className={classes.button}
variant='contained'
color='primary'
type='submit'
endIcon={<Icon/>}
onClick={handleSubmit}
>
SEND
</Button>
</form>
</Container>
</div>
);
}
export default CreateCourse;
I have a parent component that initializes the state using hooks. I pass in the state and setState of the hook into the child, but whenever I update the state in multiple children they update the state that is not the most updated one.
To reproduce problem: when you make a link and write in your info and click submit, it successfully appends to the parent state. If you add another one after that, it also successfully appends to the parent state. But when you go back and press submit on the first link, it destroys the second link for some reason. Please try it out on my codesandbox.
Basically what I want is a button that makes a new form. In each form you can select a social media type like fb, instagram, tiktok, and also input a textfield. These data is stored in the state, and in the end when you click apply changes, I want it to get stored in my database which is firestore. Could you help me fix this? Here is a code sandbox on it.
https://codesandbox.io/s/blissful-fog-oz10p
and here is my code:
Admin.js
import React, { useState } from 'react';
import Button from '#material-ui/core/Button';
import AddNewLink from './AddNewLink';
const Admin = () => {
const [links, setLinks] = useState({});
const [newLink, setNewLink] = useState([]);
const updateLinks = (socialMedia, url) => {
setLinks({
...links,
[socialMedia]: url
})
}
const linkData = {
links,
updateLinks,
}
const applyChanges = () => {
console.log(links);
// firebase.addLinksToUser(links);
}
return (
<>
{newLink ? newLink.map(child => child) : null}
<div className="container-sm">
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
onClick={() => {
setNewLink([ ...newLink, <AddNewLink key={Math.random()} linkData={linkData} /> ])}
}
>
Add new social media
</Button>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
style={{marginTop: '50px'}}
onClick={() => applyChanges()}
>
Apply Changes
</Button>
<h3>{JSON.stringify(links, null, 4)}</h3>
</div>
</>
);
}
export default Admin;
AddNewLink.js
const AddNewLink = props => {
const [socialMedia, setSocialMedia] = useState('');
const [url, setUrl] = useState('');
const { updateLinks } = props.linkData;
const handleSubmit = () => {
updateLinks(socialMedia, url)
}
return (
<>
<FormControl style={{marginTop: '30px', marginLeft: '35px', width: '90%'}}>
<InputLabel>Select Social Media</InputLabel>
<Select
value={socialMedia}
onChange={e => {setSocialMedia(e.target.value)}}
>
<MenuItem value={'facebook'}>Facebook</MenuItem>
<MenuItem value={'instagram'}>Instagram</MenuItem>
<MenuItem value={'tiktok'}>TikTok</MenuItem>
</Select>
</FormControl>
<form noValidate autoComplete="off" style={{marginBottom: '30px', marginLeft: '35px'}}>
<TextField id="standard-basic" label="Enter link" style={{width: '95%'}} onChange={e => {setUrl(e.target.value)}}/>
</form>
<div className="container-sm">
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
style={{marginBottom: '30px'}}
onClick={() => handleSubmit()}
>
Submit
</Button>
</div>
</>
)
}
export default AddNewLink;
All I see is that links in AddNewLink would be a stale closure but in your question you never use it. Here is your code "working" since you didn't describe what it is supposed to do it always "works"
const { useState } = React;
const AddNewLink = (props) => {
const [socialMedia, setSocialMedia] = useState('');
const [url, setUrl] = useState('');
const { updateLinks, links } = props.linkData;
console.log('links is a stale closure:', links);
const handleSubmit = () => {
updateLinks(socialMedia, url);
};
return (
<div>
<select
value={socialMedia}
onChange={(e) => {
setSocialMedia(e.target.value);
}}
>
<option value="">select item</option>
<option value={'facebook'}>Facebook</option>
<option value={'instagram'}>Instagram</option>
<option value={'tiktok'}>TikTok</option>
</select>
<input
type="text"
id="standard-basic"
label="Enter link"
style={{ width: '95%' }}
onChange={(e) => {
setUrl(e.target.value);
}}
/>
<button
type="submit"
variant="contained"
color="primary"
style={{ marginBottom: '30px' }}
onClick={() => handleSubmit()}
>
Submit
</button>
</div>
);
};
const Admin = () => {
const [links, setLinks] = useState({});
const [newLink, setNewLink] = useState([]);
const updateLinks = (socialMedia, url) =>
setLinks({
...links,
[socialMedia]: url,
});
const linkData = {
links,
updateLinks,
};
const applyChanges = () => {
console.log(links);
// firebase.addLinksToUser(links);
};
return (
<React.Fragment>
{newLink ? newLink.map((child) => child) : null}
<div className="container-sm">
<button
type="submit"
variant="contained"
color="primary"
onClick={() => {
setNewLink([
...newLink,
<AddNewLink
key={Math.random()}
linkData={linkData}
/>,
]);
}}
>
Add new social media
</button>
<button
type="submit"
variant="contained"
color="primary"
style={{ marginTop: '50px' }}
onClick={() => applyChanges()}
>
Apply Changes
</button>
<h3>{JSON.stringify(links, null, 4)}</h3>
</div>
</React.Fragment>
);
};
ReactDOM.render(<Admin />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
It is not a good idea to put jsx in local state, save the data in state instead and pass that to the component every render.
//Main functional component
function CustomizedTables(props) {
const classes = useStyles();
const [nameCheckbox, setnameCheckbox] = useState([]);
const [emailCheckbox, setemailCheckbox] = useState([]);
const [faxCheckbox, setfaxCheckbox] = useState([]);
const [loopStopFlag, setFlag] = useState(true);
const [emailFlag, setEmailFlag] = useState(false);
const rows = props.data;
//Initialization of checkboxes to true
if (loopStopFlag) {
let nameArray = [];
let emailArray = [];
let faxArray = [];
rows.SupplierList.forEach((supplier, i) => {
nameArray.push(true);
if (supplier.Email) {
emailArray.push(true);
} else {
emailArray.push(null);
}
if (supplier.Fax) {
faxArray.push(true);
} else {
faxArray.push(null);
}
});
setnameCheckbox(nameArray);
setemailCheckbox(emailArray);
setfaxCheckbox(faxArray);
setFlag(false);
}
//Reseting all checkboxes
const resetAllCheckbox = () => {
let nameArray = [];
let emailArray = [];
let faxArray = [];
rows.SupplierList.forEach((supplier, i) => {
nameArray.push(false);
if (supplier.Email) {
emailArray.push(false);
} else {
emailArray.push(null);
}
if (supplier.Fax) {
faxArray.push(false);
} else {
faxArray.push(null);
}
});
setnameCheckbox(nameArray);
setemailCheckbox(emailArray);
setfaxCheckbox(faxArray);
if (emailCheckbox.includes(true)) {
setEmailFlag(false);
} else {
setEmailFlag(true);
}
};
//Displaying data on screen
return (
<div>
<Paper className={classes.root} style={{ marginLeft: "32px" }}>
<Table className={classes.table} aria-label="customized table">
<TableBody>
{rows.SupplierList.map((row, i) => (
<StyledTableRow key={i}>
<StyledTableCell style={{ color: "#474747" }} align="center">
<label>
{row.Name}{" "}
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr = nameCheckbox;
arr[i] = !arr[i];
setnameCheckbox(arr);
}}
defaultChecked={nameCheckbox[i]}
/>
</label>
<br />
<div style={{ fontSize: "11px" }}>{row.Gbf && "GBF"}</div>
</StyledTableCell>
<StyledTableCell align="center" style={{ color: "#474747" }}>
{row.Email && (
<React.Fragment>
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr1 = emailCheckbox;
arr1[i] = !arr1[i];
setemailCheckbox(arr1);
if (emailCheckbox.includes(true)) {
setEmailFlag(false);
} else {
setEmailFlag(true);
}
}}
defaultChecked={emailCheckbox[i]}
/>
</React.Fragment>
)}
</StyledTableCell>
<StyledTableCell align="center" style={{ color: "#474747" }}>
{row.Fax && (
<React.Fragment>
<input
type="checkbox"
style={{ marginLeft: "5px", cursor: "pointer" }}
onChange={() => {
let arr2 = faxCheckbox;
arr2[i] = !arr2[i];
setfaxCheckbox(arr2);
}}
value={faxCheckbox[i]}
defaultChecked={faxCheckbox[i]}
/>
</React.Fragment>
)}
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</Paper>
<br />
<Button
variant="danger"
style={{ width: "100px" }}
onClick={resetAllCheckbox}
>
Reset
</Button>
</div>
);
}
export default CustomizedTables;
I am facing problem in reseting checkboxes. When I reset them only array is updating but no action is performing or checkboxes. I used both checked and defaultChecked in checked also facing problem that bool is updating but checkbox is not updating. I want a solution for it. Actually when I click on reset button then my checkboxes remain same form but state is updating
I couldn't replicate your exact code in a sandbox because of it's dependencies so I've put up a simple example that has a set of checkboxes, check action and reset action.
Modify your code based on the example and these notes and it would work as expected:
Whenever you render a react element from inside loops you should provide a unique key, this helps react identify what changed.
Keep your checkbox state in your react/store state and set it using checked prop and not defaultChecked. On reset update the store/local states and the component will re-render.
Check the sandbox:
https://codesandbox.io/s/pedantic-violet-twtvn?fontsize=14&hidenavigation=1&theme=dark
import React, { useState, useEffect } from "react";
function CheckboxDemo({ data }) {
const [features, setFeatures] = useState([]);
useEffect(() => {
setFeatures(data);
}, [data]);
const resetCheckboxes = () => {
const newFeatures = [];
features.forEach(f => {
newFeatures.push({ ...f, checked: false });
});
setFeatures(newFeatures);
};
const setFeatureCheck = index => () => {
const updatedFeature = {
...features[index],
checked: !features[index].checked
};
const newFeatures = [
...features.slice(0, index),
updatedFeature,
...features.slice(index + 1)
];
setFeatures(newFeatures);
};
const checkBoxes = features.map((f, i) => (
<div className="box" key={i + "_" + f.label}>
<input
type="checkbox"
checked={f.checked}
onChange={setFeatureCheck(i)}
/>{" "}
{f.label}
</div>
));
return (
<div className="wrapper">
{checkBoxes}
<div className="controls">
<input type="button" onClick={resetCheckboxes} value="Reset" />
</div>
</div>
);
}
export default CheckboxDemo;