I have made a table with a search bar functionality, it filter the data when press the search button and reset the filter function and show unfilter data when click the clear button but it's not clearing the current input value from the display. however it clear the filetr function and show unfilter data. I tired setting state to empty string but still not able to clear the input value, I'm new in react need assistance to understand the issue
1. App.js having search bar and all the state and function
function App() {
const [searchTerm, setSearchTerm] = useState("");
const classes = useStyles();
const [buttonSearch, setButtonSearch] = useState("");
const getSearchTerm = (event) => {
let searchWord = event.target.value;
setSearchTerm(searchWord);
console.log(searchWord);
}
const doSearch = () => {
console.log('this is from the doSearch func', searchTerm)
setButtonSearch(searchTerm);
}
const clearSearch = () => {
console.log('im working')
setSearchTerm("");
setButtonSearch("");
}
return (
<div className="App">
<div className="wrapper">
<div className="container-table">
<div className="head">
<h5 className='management'>MANAGEMENT</h5>
<div className="head-middle">
<h2>Clients</h2>
<div className="button-collection">
<Button style={{ backgroundColor: '#5900B4', color: '#FFFFFF', fontSize: '15px', fontWeight: '900', width: '206px', height: '42px' }}
variant="contained"
className='add-collection-btn'
startIcon={<AddIcon />}
>
New Collection
</Button>
</div>
</div>
<div className="head-bottom">
<div className="head-button">
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
<Button onClick={doSearch}
style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px', marginLeft: '20px', marginRight: '20px' }} variant="contained">
Search
</Button>
<Button onClick={clearSearch}
style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px' }} variant="contained">
Clear
</Button>
</div>
<Button
style={{ backgroundColor: 'transparent', color: '#5900B4', width: '206px', height: '42px', borderColor: '#5900B4', fontSize: '15px', fontWeight: '900' }}
variant="outlined" color="primary"
startIcon={<FilterListIcon />}
>
SHOW FILTER
</Button>
</div>
<div className="table">
<EnhancedTable
searchTerm={buttonSearch}
/>
</div>
</div>
</div>
</div>
</div>
);
}
2. table.js having filter and map function
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
const { searchTerm } = props;
console.log("table searchTerm value", searchTerm)
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const isSelected = (name) => selected.indexOf(name) !== -1;
const [data, setData] = useState([]);
const getData = async () => {
try {
const data = await axios.get("something");
console.log('This is data from axios', data.data);
setData(data.data);
} catch (e) {
console.log("this is error for fetching data", e)
}
};
useEffect(() => {
getData();
}, [])
const filtered = useMemo(() => {
if (!searchTerm) {
return data;
}
const term = searchTerm.toLowerCase()
return data.filter(({ clientName, clientEmail }) => clientName.toLowerCase().includes(term)
|| clientEmail.toLowerCase().includes(term)
)
}, [data, searchTerm])
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
order={order}
orderBy={orderBy}
/>
<TableBody>
{filtered
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right">
<Button style={{ backgroundColor: 'transparent', color: '#5900B4' }}
variant="outlined" color="primary" href="#outlined-buttons" >
{<CreateIcon />}
</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
);
}
Try this option
<InputBase value={searchTerm}
Related
why is my code showing wrong output for
as the rows has been already populated after adding it with the handletabledata function
My code is having a table and there is a add button where we add users but on the delete function when I am trying to see the rows I am getting only that row and rows above it.
please help.
import React from 'react'
import './server.scss'
import Paper from '#mui/material/Paper';
import Table from '#mui/material/Table';
import TableBody from '#mui/material/TableBody';
import TableCell from '#mui/material/TableCell';
import TableContainer from '#mui/material/TableContainer';
import TableHead from '#mui/material/TableHead';
import TablePagination from '#mui/material/TablePagination';
import TableRow from '#mui/material/TableRow';
import { Button } from '#mui/material';
import DeleteOutlineIcon from '#mui/icons-material/DeleteOutline';
import Dialog from '#mui/material/Dialog';
import DialogActions from '#mui/material/DialogActions';
import DialogContent from '#mui/material/DialogContent';
import DialogContentText from '#mui/material/DialogContentText';
import DialogTitle from '#mui/material/DialogTitle';
import Slide from '#mui/material/Slide';
import PersonAddAltIcon from '#mui/icons-material/PersonAddAlt';
import TextField from '#mui/material/TextField';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
const columns = [
{ id: 'key', label: '#', minWidth: 70 },
{ id: 'user', label: 'User', minWidth: 100 },
{
id: 'signed',
label: 'Last Signed in',
minWidth: 170,
align: 'right',
},
{
id: 'role',
label: 'Role',
minWidth: 170,
align: 'right',
},
{
id: 'icon',
label: '',
minWidth: 170,
align: 'right',
},
];
function createData(user, role, key, icon, signed) {
signed = signed.getTime()
return { key, user, signed, role, icon };
}
//modal code
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
const Server = () => {
//final insertion of data
const [rows, setrow] = React.useState([]);
// console.log(rows)
//pagination
const [page, setPage] = React.useState(0);
const [rowsPerPage, setRowsPerPage] = React.useState(10);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setRowsPerPage(+event.target.value);
setPage(0);
};
//modal code
const [open, setOpen] = React.useState(false);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
//setting data for row
const [id, setid] = React.useState(0);
const handletabledata = () => {
setid(id + 1)
let row = [...rows]
row.push(createData(email, role, id, <DeleteOutlineIcon onClick={console.log(rows)}/>, new Date()))
setrow(row)
setemail('')
setrole('')
setOpen(false);
}
//input field
const [email, setemail] = React.useState('');
const handleEmail = (event) => {
setemail(event.target.value);
};
//dropdown list
const [role, setrole] = React.useState('');
const handleChange = (event) => {
setrole(event.target.value);
};
return (
<div className='server_main'>
<div className='addUser'>
<Button variant="contained" onClick={handleClickOpen}>Add User</Button>
<Dialog
open={open}
TransitionComponent={Transition}
keepMounted
onClose={handleClose}
aria-describedby="alert-dialog-slide-description"
fullWidth={true}
>
<DialogTitle>{"Add User"}</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
<div style={{ display: 'flex', height: '250px', justifyContent: 'space-around' }}>
<div >
<PersonAddAltIcon style={{ fontSize: '7rem', position: 'relative', top: '50px' }} />
</div>
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'space-around' }}>
<span style={{ padding: '10px', position: 'relative', left: '10px', top: '10px' }}>User Information</span>
<TextField placeholder="Email Address" onChange={handleEmail} value={email} />
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Role</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={role}
label="role"
onChange={handleChange}
>
<MenuItem value={'Owner'}>Owner</MenuItem>
<MenuItem value={'Sales'}>Sales</MenuItem>
<MenuItem value={'Admin'}>Admin</MenuItem>
</Select>
</FormControl>
</div>
</div>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button onClick={handletabledata}>Add</Button>
</DialogActions>
</Dialog>
</div>
<Paper sx={{ width: '100%', overflow: 'hidden' }}>
<TableContainer sx={{ maxHeight: 440 }}>
<Table stickyHeader aria-label="sticky table">
<TableHead>
<TableRow>
{columns.map((column) => (
<TableCell
key={column.id}
align={column.align}
style={{ minWidth: column.minWidth }}
>
{column.label}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{rows
.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
.map((row) => {
return (
<TableRow hover role="checkbox" tabIndex={-1} key={row.code}>
{columns.map((column) => {
const value = row[column.id];
return (
<TableCell key={column.id} align={column.align}>
{column.format && typeof value === 'number'
? column.format(value)
: value}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
<TablePagination
rowsPerPageOptions={[10, 25, 100]}
component="div"
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</Paper>
</div>
);
}
export default Server
I am trying to make a search input that will filter by the name user will input. I have two components one is having the search input (app.js) and the other is having the table(table.js).
I am able to get the current value of the search input (app.js) but when trying to pass it as a props in the table (table.js) it's giving an error.
I am new to react but from my understanding, I think I'm having issues with passing the useState as a props.
1. Code for search input (app.js)
I used onChange event as to get the current value when the input field change.
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
getSearchTerm function to get current input value
const [searchTerm, setSearchTerm] = useState("");
const getSearchTerm = (event) => {
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
2. Table (app.js) passing the props
passing the props in the filter function. So can get filterd when a input would be entered in search input.
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
<TableBody>
{data
.filter((item) => {
if (props.searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(props.searchTerm.toLowerCase())) {
return item;
}
})
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
3. Error getting
[The error I'm getting is here][1]
4. Full Search input (app.js) file for more clarity
function App() {
const [searchTerm, setSearchTerm] = useState("");
const classes = useStyles();
const getSearchTerm = (event) => {
//console.log(inputEl.current.value);
const searchWord = event.target.value;
console.log(searchWord);
setSearchTerm(searchWord)
}
return (
<div className="App">
<div className="wrapper">
<div className="container-table">
<div className="head">
<h5 className='management'>MANAGEMENT</h5>
<div className="head-middle">
<h2>Clients</h2>
<div className="button-collection">
<Button style={{ backgroundColor: '#5900B4', color: '#FFFFFF', fontSize: '15px', fontWeight: '900', width: '206px', height: '42px' }}
variant="contained"
className='add-collection-btn'
startIcon={<AddIcon />}
>
New Collection
</Button>
</div>
</div>
<div className="head-bottom">
<div className="head-button">
<div className="search">
<div className={classes.search}>
<div className={classes.searchIcon}>
<SearchIcon />
</div>
<InputBase
placeholder="Search..."
classes={{
root: classes.inputRoot,
input: classes.inputInput,
}}
onChange={getSearchTerm}
/>
</div>
</div>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px', marginLeft: '20px', marginRight: '20px' }} variant="contained">Search</Button>
<Button style={{ backgroundColor: 'white', color: 'black', width: '100px', height: '40px' }} variant="contained">Clear</Button>
</div>
<Button style={{
backgroundColor: 'transparent', color: '#5900B4', width: '206px', height: '42px', borderColor: '#5900B4', fontSize: '15px', fontWeight: '900'
}} variant="outlined" color="primary"
startIcon={<FilterListIcon />}
>
SHOW FILTER
</Button>
</div>
<div className="table">
<EnhancedTable
onChange={setSearchTerm}
/>
</div>
</div>
</div>
</div>
</div>
);
}
export default App;
5. Full table (table.js) file for clarity
const headCells = [
{ id: 'name', numeric: false, disablePadding: true, label: 'Client Name' },
{ id: 'email', numeric: true, disablePadding: false, label: 'Email' },
{ id: 'phone', numeric: true, disablePadding: false, label: 'Phone' },
{ id: 'industry', numeric: true, disablePadding: false, label: 'Industry' },
{ id: 'contact', numeric: true, disablePadding: false, label: 'Point of Contact' },
{ id: 'website', numeric: true, disablePadding: false, label: 'Website' },
{ id: 'btn-icon', numeric: true, disablePadding: false, label: '' },
];
function EnhancedTableHead(props) {
const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount } = props;
return (
<TableHead>
<TableRow style={{ backgroundColor: '#F5F6F8', height: '120px' }}>
<TableCell padding="checkbox">
<Checkbox
indeterminate={numSelected > 0 && numSelected < rowCount}
checked={rowCount > 0 && numSelected === rowCount}
onChange={onSelectAllClick}
inputProps={{ 'aria-label': 'select all desserts' }}
/>
</TableCell>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.numeric ? 'right' : 'left'}
padding={headCell.disablePadding ? 'none' : 'normal'}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
EnhancedTableHead.propTypes = {
classes: PropTypes.object.isRequired,
numSelected: PropTypes.number.isRequired,
order: PropTypes.oneOf(['asc', 'desc']).isRequired,
orderBy: PropTypes.string.isRequired,
};
const useToolbarStyles = makeStyles((theme) => ({
root: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(1),
},
highlight:
theme.palette.type === 'light'
? {
color: theme.palette.secondary.main,
backgroundColor: lighten(theme.palette.secondary.light, 0.85),
}
: {
color: theme.palette.text.primary,
backgroundColor: theme.palette.secondary.dark,
},
title: {
flex: '1 1 100%',
},
}));
const EnhancedTableToolbar = (props) => {
const classes = useToolbarStyles();
return (
<Toolbar>
{
<Typography className={classes.title} variant="h6" id="tableTitle" component="div">
Clients
</Typography>
}
</Toolbar>
);
};
EnhancedTableToolbar.propTypes = {
numSelected: PropTypes.number.isRequired,
};
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
},
paper: {
width: '100%',
marginBottom: theme.spacing(2),
},
table: {
minWidth: 750,
},
visuallyHidden: {
border: 0,
clip: 'rect(0 0 0 0)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
top: 20,
width: 1,
},
}));
export default function EnhancedTable(props) {
console.log("these r props for table component", props);
const classes = useStyles();
const [order, setOrder] = React.useState('asc');
const [orderBy, setOrderBy] = React.useState('calories');
const [selected, setSelected] = React.useState([]);
const [page, setPage] = React.useState(0);
const [dense, setDense] = React.useState(false);
const [rowsPerPage, setRowsPerPage] = React.useState(5);
const isSelected = (name) => selected.indexOf(name) !== -1;
const [data, setData] = useState([]);
const getData = async () => {
try {
const data = await axios.get("something");
setData(data.data);
} catch (e) {
console.log("this is error for fetching data", e)
}
};
useEffect(() => {
getData();
}, [])
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<EnhancedTableToolbar numSelected={selected.length} />
<TableContainer>
<Table
className={classes.table}
aria-labelledby="tableTitle"
size={dense ? 'small' : 'medium'}
aria-label="enhanced table"
>
<EnhancedTableHead
classes={classes}
numSelected={selected.length}
order={order}
orderBy={orderBy}
/>
<TableBody>
{data
/*.filter((item) => {
if (searchTerm == "") {
return item;
} else if (item.clientName.toLowerCase().includes(searchTerm.toLowerCase())) {
return item;
}
})*/
.map((item, index) => {
return (
<TableRow
hover
role="checkbox"
tabIndex={-1}
>
<TableCell padding="checkbox">
<Checkbox
/>
</TableCell>
<TableCell component="th" scope="row" padding="none">{item.clientName}</TableCell>
<TableCell align="right">{item.clientEmail}</TableCell>
<TableCell align="right">{item.clientWorkPhone}</TableCell>
<TableCell align="right">{item.clientIndustry}</TableCell>
<TableCell align="right">{item.tenantId}</TableCell>
<TableCell align="right">{item.clientWebsite}</TableCell>
<TableCell align="right"><Button style={{ backgroundColor: 'transparent', color: '#5900B4' }} variant="outlined" color="primary" href="#outlined-buttons" >{<CreateIcon />}</Button>
</TableCell>
</TableRow>
)
})}
</TableBody>
</Table>
</TableContainer>
</Paper>
</div>
);
}
The issue is that searchTerm is not within scope of your component so this line...
item.clientName.toLowerCase().includes(searchTerm.toLowerCase())
throws the error you're seeing
Cannot read property 'toLowerCase' of undefined
You need to pass in the required props to your component.
<EnhancedTable searchTerm={searchTerm} />
This is also a perfect opportunity to use React's useMemo hook to produce the filtered results
const { searchTerm } = props // extract searchTerm from props
const filtered = useMemo(() => {
if (!searchTerm) {
return data
}
const term = searchTerm.toLowerCase()
return data.filter(({ clientName }) =>
clientName.toLowerCase().includes(term))
}, [ data, searchTerm ])
Now you can just use filtered in place of data in your return value
<TableBody>
{filtered.map((item, index) => {
// etc
})}
</TableBody>
I tried doing this by creating a text that overlays the textbox but I did not continue because it will further complicate things.
I rollback the code.
#########################################################################################
Here is my rollbacked code:
const StepFour = (props) => {
const [number, setNumber] = useState('')
const [expiry, setExpiry] = useState('')
const [cvc, setCvc] = useState('')
const [focus, setFocus] = useState('')
const [month, setMonth] = useState('')
const [year, setYear] = useState('')
const [cardNumError, setCardNumError] = useState(false)
const dispatch = useDispatch()
const cardList = ['4571736000000075']
const handleCardNumError = () => {
if (!cardList.includes(number) ) {
setCardNumError(true)
} else {
setCardNumError(false)
}
}
const handleCardNumError2 = () => {
if (!cardList.includes(number) && number.length === 19 ) {
setCardNumError(true)
} else {
setCardNumError(false)
}
}
useEffect(() => {
setExpiry(month + '/' + year)
}, [month, year])
const classes = useStyles()
return (
<div className={classes.mainContainer}>
<div>
<Cards
number={number}
expiry={expiry}
cvc={cvc}
focused={focus}/>
</div>
<div className={classes.formInputContatiner}>
<div>
<Typography
variant="subtitle1"
style={{
color: "black",
textAlign: "left",
marginBottom: "5px"
}}>
Card Number
</Typography>
<div className={classes.cardNumber}>
<div className={classes.grid}>
<div className={classes.cardNumContainer}>
<input
className={classes.inputStyle}
type='tel'
name='number'
maxLength='16'
placeholder='1234 1234 1234 1234'
value={number}
style={{
color: "black",
border: "1px solid",
borderColor: cardNumError ? 'red' : '#ccc',
display: "inline-block"
}}
onChange={e => {
let val = e.target.value
const re = /^[0-9\b]+$/
if (val === '' || re.test(val)) {
setNumber(val)
dispatch(getCN(val))
}
handleCardNumError2()
}}
onFocus={e => {
setFocus(e.target.name)
}}
onBlur={() => {
handleCardNumError()
}}/>
</div>
</div>
{cardNumError === true ? <Typography
variant="subtitle1"
style={{
color: "red",
}}>
Your card number is invalid.
</Typography> : null}
</div>
</div>
<div>
<Typography
variant="subtitle1"
style={{
color: "black",
textAlign: "left",
marginBottom: "5px"
}}>
Month and Year
</Typography>
<div className={classes.dateAndCVC}>
<input
className={classes.inputStyle2}
type="text"
name='month'
placeholder='MM'
value={month}
onChange={e => {
setMonth(e.target.value)
dispatch(getCDM(e.target.value))
}}
onFocus={e => setFocus(e.target.name)}/>
<input
className={classes.inputStyle2}
type="text"
name='year'
placeholder='YY'
value={year}
onChange={e => {
setYear(e.target.value)
dispatch(getCDY(e.target.value))
}}
onFocus={e => setFocus(e.target.name)}/>
</div>
</div>
<div>
<Typography
variant="subtitle1"
style={{
color: "black",
textAlign: "left",
marginBottom: "5px"
}}>
CVC
</Typography>
<input
className={classes.inputStyle2}
type="tel"
name='cvc'
placeholder='CVC'
value={cvc}
onChange={e => {
setCvc(e.target.value)
dispatch(getCCVC(e.target.value))
}}
onFocus={e => setFocus(e.target.name)}/>
</div>
</div>
</div>
)
}
export default StepFour
Here is the output:
This is my solution. I handle when the card will display by check if the input has already 4 characters length and then add space in it.
The important part is when you type each character you have to remove all old space before add space again otherwise it will fill the input with a lot of weird space.
Here is my code. You can see its action here
import React, { useState } from 'react';
function App() {
const [card, setCard] = useState('')
const handleCardDisplay = () => {
const rawText = [...card.split(' ').join('')] // Remove old space
const creditCard = [] // Create card as array
rawText.forEach((t, i) => {
if (i % 4 === 0 && i !== 0) creditCard.push(' ') // Add space
creditCard.push(t)
})
return creditCard.join('') // Transform card array to string
}
return (
<div>
<input
value={handleCardDisplay()}
onChange={(e) => setCard(e.target.value)}
/>
</div>
)
}
export default App;
Goal
Dynamically create dropdown menus via hooks.
Challenge
When I change a hook value to my dropdown, the dropdown does not open or close. Its stays closed.
When I hard code the dropdown in the return, the open and close functions correctly.
Doesn't work Using hooks
///Various required imports are here..
export default function main(){
const [testb, state_set_testb] = useState(<div></div>);
function toggle_dropdown_function(toggle_request) {
console.log("fffffffffffffffffffffff",toggle_request)
state_set_dropdown_open(toggle_request)
}
state_set_testb(<div onMouseEnter={() => toggle_dropdown_function(true)} onMouseLeave={() => toggle_dropdown_function(false)}>
<Dropdown id="testa" isOpen={dropdownOpen} toggle={toggle_dropdown}>
<DropdownToggle style={{ backgroundColor: "transparent", border: "none", paddingTop: '8px', paddingBottom: '8px', paddingleft: '10px', paddingRight: '10px', color: "grey", fontSize: "14px " }}
color="light" >
othera
</DropdownToggle >
<DropdownMenu >
<DropdownItem style={{ fontSize: "14px " }}>Some Action</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>)
return <div>{testb}</div>
}
Works Not using hooks
///Various required imports are here..
export default function main(){
function toggle_dropdown_function(toggle_request) {
console.log("fffffffffffffffffffffff",toggle_request)
state_set_dropdown_open(toggle_request)
}
return <div onMouseEnter={() => toggle_dropdown_function(true)} onMouseLeave={() => toggle_dropdown_function(false)}>
<Dropdown id="testa" isOpen={dropdownOpen} toggle={toggle_dropdown}>
<DropdownToggle style={{ backgroundColor: "transparent", border: "none", paddingTop: '8px', paddingBottom: '8px', paddingleft: '10px', paddingRight: '10px', color: "grey", fontSize: "14px " }}
color="light" >
othera
</DropdownToggle >
<DropdownMenu >
<DropdownItem style={{ fontSize: "14px " }}>Some Action</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
}
Here is how you could accomplish it. Essentially you have a separate component called Dropdown and you push it to an array of dropdowns.
const { useState } = React;
const Dropdown = () => {
const [active, setActive] = useState('Select');
const [isOpen, setOpen] = useState(false);
const items = ["orange", "pear", "apple"];
return <div className={`dropdown`}>
<div onClick={()=> setOpen(!isOpen)} className={"dropdown__header"} >{active}</div >
{isOpen &&
<div className={"dropdown__body"}>
{items.map((item, index) => {
return <div key={index} onClick={(e) => {
setActive(item);
setOpen(false);
}}>{item}</div>
})}
</div>
}
</div>
}
const Main = () => {
const [dropdowns, setDropdowns] = useState([])
const addDropdowns = () => {
let updatedDropdowns = [...dropdowns];
updatedDropdowns.push(<Dropdown />)
setDropdowns(updatedDropdowns);
}
return (
<div className={"main"}>
<button onClick={addDropdowns}>
Add Dropdown
</button>
{dropdowns.map((dropdown, index) => {
return <div key={index}>{dropdown}</div>
})}
</div>
)
}
ReactDOM.render(<Main />, document.getElementById('app'))
Here is some codepen.
UPDATE
I managed to use reactstrap using the same approach and I did not notice any problems.
Here is a codepen
Is there another way to do this without having to place the useState variables outside the map function?
const slimy = [];
{
slimy.map(function (item) {
const [count, setCount] = useState("");
return (
<ListItem
key={item.createdAt}
style={{ display: "flex", justifyContent: "center" }}
>
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)} />
</div>
</ListItem>
);
});
}
You can define a custom component.
{
slimy.map(function (item) {
return (
<CustomItem key={item.createdAt} names={item.names} />
);
});
}
const CustomItem = (props) => {
console.log(props.names); // <----------------
const [count, setCount] = useState(0);
return (
<ListItem style={{ display: "flex", justifyContent: "center" }}>
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)} />
</div>
</ListItem>
)
}
You can put an array into state instead:
const [countArr, setCountArr] = useState(
() => new Array(slimy.length).fill(0)
);
return slimy.map(function(item, i){
<ListItem key={item.createdAt} style={{ display: 'flex', justifyContent: 'center'}}>
<div>
<p>{countArr[i]}</p>
<button onClick={() => { setCountArr(
countArr.map((count, j) => i === j ? count + 1 : count)
) }} />
</div>
</ListItem>)
});