I want to make an editable piece of display text, such that:
The text itself is not a textarea/input, it is just standard text
Clicking on the text will open a text area and edit button, which allows you to edit the text
Clicking the edit button will update the display text, and hide the textarea/edit button (display:none)
I have implemented every part of this, and now have a bug where any time I hit the 'edit' button, all of the text components are erased from the page entirely.
To achieve this, I have a Page component which stores a series of EditableText components (you can add more EditableText components via a + button at the bottom of the page):
const Page = (props) => {
const classes = useStyles()
var [components, setComponents] = useState([])
var [displayButton, setDisplayButton] = useState(false)
const toggleButton = () => {
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addCaution = () => {
setComponents(components.concat(<Caution />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const addImportant = () => {
setComponents(components.concat(<Important />))
displayButton = !displayButton
setDisplayButton(displayButton)
}
const opbutton = (
<div>
<Button onClick={addHeader}>header</Button>
<Button onClick={addText}>text</Button>
<Button onClick={addCaution}>caution</Button>
<Button onClick={addImportant}>important</Button>
</div>
)
return (
<div className={classes.page}>
{components}
{displayButton ? opbutton : null}
<Button onClick={toggleButton}>+</Button>
</div>
)
The EditableText components are added into an array via useState.
Here is the code for the EditableText:
const EditableText = (props) => {
const classes = useStyles()
const { inputs, handleInputChange, handleSubmit } = useSubmit()
var [displayText, setDisplayText] = useState("click here to add notes!")
var [showBox, setShowBox] = useState(false)
var [showClickArea, setClickArea] = useState(true)
const inputBox = (
<form
onSubmit={handleSubmit}
style={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<textarea
type="text"
name="displayText"
className={classes.textbox}
onChange={handleInputChange}
value={inputs}
style={{ borderColor: props.border }}
onSubmit={"return inputs"}
>
{inputs}
</textarea>
<Button id="editbutton" type="submit" className={classes.button}>
edit
</Button>
</form>
)
const toggleEdit = () => {
showClickArea = !showClickArea
setClickArea(showClickArea)
showBox = !showBox
setShowBox(showBox)
}
const clickArea = (
<div
style={{ width: "80%", height: "200%", position: "absolute" }}
onClick={toggleEdit}
/>
)
return (
<div>
{showClickArea ? clickArea : null}
<div className={classes.background} style={{ color: props.color }}>
{inputs}
<div>{showBox ? inputBox : displayText}</div>
</div>
</div>
)
}
You may notice the use of useSubmit which is a custom hook:
const useSubmit = (callback) => {
const [input, setInput] = useState({})
const handleSubmit = (event) => {
callback()
}
const handleInputChange = (event) => {
event.persist()
setInput((input) => ({
...input,
[event.target.name]: [event.target.value],
}))
}
return (handleSubmit, handleInputChange, input)
}
What I figure is that this may be an issue with the custom hook, or the use of form and submit. I think form is supposed to clear the page once submitted. But I'm not entirely sure. Any idea how to prevent this?
Solved by creating a function and an 'edit' variable:
function setDisplayState() {
if (edit === false) {
return (
<div
style={{
color: props.color,
fontWeight: props.weight ? props.weight : 400,
fontSize: props.size ? props.size : 14,
}}
>
{displayText}
</div>
)
} else {
return (
<div>
<textarea
className={classes.textbox}
style={{ color: props.color }}
value={displayText}
onChange={(e) => {
displayText = e.target.value
setDisplayText(displayText)
}}
/>
<Button className={classes.button} onClick={saveChange}>
save
</Button>
</div>
)
}
}
Related
I want to add filter, search to ant design tables. Based on the documentation, we need to add some props to table column but it will be repeatative. Is there a way that we can creat a seperate component for the columns and filtering those columns?
I tried to develop that component but the column props' do not accept JSX.
I've done it in this way:
SearchHighliter:
const HighlighterWrapper = memo(({ searchWords, textToHighlight }) => (
<Highlighter
highlightStyle={{
backgroundColor: '#ffc069',
padding: 0,
}}
searchWords={searchWords}
autoEscape
textToHighlight={textToHighlight}
/>
))
FilterHook:
import { useRef, useState } from 'react'
import { Button, Input, Space } from 'antd'
import { SearchOutlined } from '#ant-design/icons'
const useTableFilter = () => {
const [searchText, setSearchText] = useState('')
const [searchedColumn, setSearchedColumn] = useState('')
const searchInput = useRef(null)
const handleSearch = (selectedKeys, confirm, dataIndex) => {
confirm()
setSearchText(selectedKeys[0])
setSearchedColumn(dataIndex)
}
const handleReset = (clearFilters, confirm) => {
clearFilters()
setSearchText('')
confirm()
}
const getColumnSearchProps = (dataIndex) => ({
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
<div
style={{
padding: 8,
}}
>
<Input
ref={searchInput}
placeholder="Search text"
value={selectedKeys[0]}
onChange={(e) => setSelectedKeys(e.target.value ? [e.target.value] : [])}
onPressEnter={() => handleSearch(selectedKeys, confirm, dataIndex)}
style={{
marginBottom: 8,
display: 'block',
}}
/>
<Space>
<Button
type="primary"
onClick={() => handleSearch(selectedKeys, confirm, dataIndex)}
icon={<SearchOutlined />}
size="small"
style={{
width: 90,
}}
>
Search
</Button>
<Button
onClick={() => clearFilters && handleReset(clearFilters, confirm)}
size="small"
style={{
width: 90,
}}
>
Reset
</Button>
</Space>
</div>
),
filterIcon: (filtered) => (
<SearchOutlined
style={{
color: filtered ? '#1890ff' : undefined,
marginRight: 10,
}}
/>
),
onFilterDropdownVisibleChange: (visible) => {
if (visible) {
setTimeout(() => searchInput.current?.select(), 100)
}
},
})
return [searchText, searchedColumn, getColumnSearchProps]
}
export default useTableFilter
Table:
const SummaryReport = () => {
const [data, setData] = useState([])
const [isLoading, setIsLoading] = useState(false)
const [searchText, searchedColumn, getColumnSearchProps] = useTableFilter()
const columns = [
{
title: () => <span style={{ paddingLeft: 8 }}>Name</span>,
key: 'name',
dataIndex: 'name',
className: 'no-padding-cell-report',
width: 250,
...getColumnSearchProps('name'),
onFilter: (value, record) => get(record, 'name').toString().toLowerCase().includes(value.toLowerCase()),
render: (_, record) => (
<div style={{ padding: 8 }}>
{searchedColumn === 'name' ? (
<HighlighterWrapper
searchWords={[searchText]}
textToHighlight={get(record, 'name') ? get(record, 'name').toString() : ''}
/>
) : (
get(record, 'name')
)}
</div>
),
},
]
....
}
All you need is just provide 2 functions to your column:
onFilter - here you describe your filter logic, if your data object is simple like in my case, the function also is trivial
render - you need to provide this function if you want to highlight text that the user inputs to filter input.
I am trying to add a functionality wherein when a user clicks on add channel button he can add the channel and it will show the newly added channel name in the respective teams. Similarly, when a user wants to delete a channel he can delete it so by clicking on delete button.
However, I figured out most of the code but seems like when am updating my newchannel list using useState it is showing me the error saying teams.channels.map is undefined and can not read properties.
If anyone can help on this would really be helpful and appreciated.
Please find the source code link below
Codesandbox link
You should create a InputAddChannel component
const InputAddChannel = ({ handleAddChannel }) => {
const [inputChannelValue, setInputChannelValue] = useState("");
const handlInputChange = (event) => {
event.preventDefault();
const newChannelName = event.target.value;
setInputChannelValue(newChannelName);
};
return (
<>
<input
placeholder="add channel"
value={inputChannelValue}
onChange={handlInputChange}
/>
<button
disabled={!inputChannelValue}
onClick={() => handleAddChannel(inputChannelValue)}
>
Add channel
</button>
</>
);
};
and pass handleAddChannel function to it
const App = () => {
const [newchannel, setNewChannel] = useState(teams);
const [addteam, setAddteam] = useState("");
const handleAddChannel = (team, i) => {
const newData = newchannel.map((channel, index) => {
if (i === index)
return {
...channel,
channels: [
...channel.channels,
{
channelName: team,
channelid:
channel.channels[channel.channels.length - 1]?.channelid + 1 ||
1
}
]
};
return channel;
});
setNewChannel(newData);
};
const handleDeleteChannel = (cid, teamid) => {
const newData = newchannel.map((channel, index) => {
if (index === teamid)
return {
...channel,
channels: channel.channels.filter((c, i) => i !== cid)
};
return channel;
});
setNewChannel(newData);
};
const handleAddteam = (event) => {
event.preventDefault();
const addteaminput = event.target.value;
setAddteam(addteaminput);
};
const handlSubmitAddteam = () => {
const newaddedteam = newchannel.concat({
name: addteam,
channels: [
{
channelid: newchannel.length + 1,
channelName: "new"
}
]
});
setNewChannel(newaddedteam);
};
return (
<>
<div className="App">
{newchannel &&
newchannel.map((team, i) => (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "0.5rem",
alignContent: "center",
justifyContent: "center",
border: "1px solid black",
padding: "0.5rem"
}}
key={i}
>
<h1> {team.name} </h1>
<InputAddChannel
handleAddChannel={(value) => handleAddChannel(value, i)}
/>
<div>
{team.channels.map((c, cid) => (
<div
style={{
display: "flex",
gap: "1rem",
alignItems: "center",
justifyContent: "center",
border: "1px solid lightgray"
}}
key={cid}
>
<h6> {c.channelName}</h6>
<button
style={{ width: "5rem", height: "2rem" }}
onClick={() => handleDeleteChannel(cid, i)}
>
Delete
</button>
</div>
))}
</div>
</div>
))}
<input
placeholder="add team"
value={addteam}
onChange={handleAddteam}
/>
<button disabled={!addteam} onClick={handlSubmitAddteam}>
Add team
</button>
</div>
</>
);
};
You can check in my codesandbox. Hope it help!
I'm not 100% sure what's going on here. I've got a display component that displays a bunch of cards, using a map based on my database - On the card is an edit button that pops a modal up, passing props over to the edit form.. Here's kinda how it looks:
import React, { useState } from 'react'
import { useQuery, useMutation } from '#apollo/client'
import { GET_ALL_PROJECTS, REMOVE_PROJECT } from '../helpers/queries'
import { makeStyles } from '#material-ui/core/styles'
import DeleteIcon from '#material-ui/icons/Delete'
import EditIcon from '#material-ui/icons/Edit'
import AddForm from './AddForm'
import EditForm from './EditForm'
import AlertMessage from '../Alerts/AlertMessage'
import { Grid, Typography, Card, CardActionArea, CardActions, CardContent, CardMedia, Button, Modal, Backdrop, Fade } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DisplayProjects = () => {
const styles = useStyles()
const [deleteItem] = useMutation(REMOVE_PROJECT)
const { loading, error, data } = useQuery(GET_ALL_PROJECTS)
const [status, setStatusBase] = useState('')
const [resultMessage, setResultMessage] = useState('')
const [addOpen, setAddOpen] = useState(false)
const [editOpen, setEditOpen] = useState(false)
const onDelete = (id, e) => {
e.preventDefault()
deleteItem({
variables: { id },
refetchQueries: [{ query: GET_ALL_PROJECTS }]
}).then(
res => handleSuccess(res),
err => handleError(err)
)
}
// Handles Result of the Delete Operation
const handleSuccess = (res) => {
console.log(res.data.deleteProject.proj_name)
// console.log('success!');
setResultMessage(res.data.deleteProject.proj_name)
setStatusBase({
msg: `Successfully Deleted ${resultMessage}`,
key: Math.random()
})
}
const handleError = (err) => {
console.log('error')
}
//Handles the Modal for Add Project
const handleAddOpen = () => {
setAddOpen(true);
};
const handleAddClose = () => {
setAddOpen(false);
};
//Handles the Modal for Edit Project
const handleEditOpen = () => {
setEditOpen(true);
};
const handleEditClose = () => {
setEditOpen(false);
};
if (loading) return '...Loading'
if (error) return `Error: ${error.message}`
return (
<div>
<div style={{ marginTop: 20, padding: 30 }}>
<Grid container spacing={8} justify='center' alignItems='center'>
{data.projects.map(p => {
return (
<Grid item key={p._id}>
<Card >
<CardActionArea>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CardMedia
style={{ width: 400, height: 100, paddingTop: 10, }}
component='img'
alt='Project Image'
height='140'
image={require('../../images/html-css-javascript-lg.jpg')}
/>
</div>
<CardContent >
<Typography gutterBottom variant='h5' component="h2">
{p.proj_name}
</Typography>
<Typography component='p'>
{p.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button>
<DeleteIcon onClick={e => onDelete(p._id, e)} />
</Button>
<Button onClick={handleEditOpen}>
<Modal
open={editOpen}
onClose={handleEditClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={editOpen}>
<div className={styles.paper}>
<EditForm
id={p._id}
close={handleEditClose}
name={p.proj_name}
desc={p.description}
gh={p.gh_link}
live={p.live_link}
img={p.image_url}
/>
</div>
</Fade>
</Modal>
<EditIcon />
</Button>
</CardActions>
</Card>
{ status ? <AlertMessage key={status.key} message={status.msg} /> : null}
</Grid>
)
}
)}
</Grid>
<Button type='button' onClick={handleAddOpen}>Add Project</Button>
<Modal
open={addOpen}
onClose={handleAddClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={addOpen}>
<div className={styles.paper}>
<AddForm close={handleAddClose} />
</div>
</Fade>
</Modal>
</div>
</div >
)
}
export default DisplayProjects
And here's the form. I've destructured out the props into variables and placed them into a state object called details, so they can be overwritten and submitted to the database..
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useMutation, useQuery } from '#apollo/client'
import { EDIT_PROJECT, GET_ALL_PROJECTS, GET_PROJECT_BY_ID} from '../helpers/queries'
const AddForm = (props) => {
const params = useParams()
const id = params.toString()
// console.log(id);
const [editProjectItem] = useMutation(EDIT_PROJECT)
const {loading, data, error} = useQuery(GET_PROJECT_BY_ID, {
variables: {
id
},
})
const [details, setDetails] = useState({})
if (loading) return '...Loading';
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
setDetails(data.projectById)
console.log(data.projectById)
const submitForm = e => {
e.preventDefault()
try {
editProjectItem({
variables: { id, proj_name, description, gh_link, live_link, image_url},
refetchQueries: [{query: GET_ALL_PROJECTS}]
})
}
catch (err) {
console.log('You Goofed')
}
// setDetails({
// proj_name: '',
// description: '',
// gh_link: '',
// live_link: '',
// image_url: ''
// })
props.close()
}
const changeDetails = (e) => {
setDetails({
...details,
[e.target.name]: e.target.value
})
}
const {_id, proj_name, description, gh_link, live_link, image_url} = details
return (
<div key = {_id}>
<h2>Edit {proj_name}</h2>
<form onSubmit = {submitForm} >
<label>
Project Name:
<input
name = 'proj_name'
value = {proj_name}
onChange = {changeDetails}
/>
</label>
<label>Description</label>
<input
name = 'description'
value = {description}
onChange = {changeDetails}
/>
<label>GitHub Link</label>
<input
name = 'gh_link'
value = {gh_link}
onChange = {changeDetails}
/>
<label>Live Link</label>
<input
name = 'live_link'
value = {live_link}
onChange = {changeDetails}
/>
<label>Preview Image</label>
<input
name = 'image_url'
value = {image_url}
onChange = {changeDetails}
/>
<button type = 'submit'>Submit</button>
</form>
</div>
)
}
export default AddForm
The problem I'm running into, is that when I access the modal, the props are sent from literally EVERY Object, instead of the one, and displays the data for the last record instead of the one I want to edit You can see what happens here (I logged props.id in order to test) https://imgur.com/a/pcEKl89
What did I miss? (Disclaimer: I am still a student, and learning the craft.. be gentle on my code please)
EDIT: I just realized that I didn't indicate that this is the final form of the EditForm component. I haven't added the logic in to make the updates yet, I just wanted to get the data showing properly first.
EDIT2: I made some changes to how the ID is passed over, I was already using React-Router, so I went ahead and made a route to /edit/:id and then using useParams(), I got the ID that way. It seems to be working, however now I'm getting a Too many re-renders message. Updated the AddForm code above to reflect the changes..
I figured out the re-render issue.. it was as simple as dropping the setDetails function into a useEffect Hook:
useEffect(()=> {
if(data){
setDetails(data.projectById)
}
},[data])
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;