How to create checkbox in A React Function - javascript

I am trying to create a Checkbox to display and hide my React Table with some data in it. I have only used classes and am new to using Functions and as a result am struggling to implement a way to create a checkbox that can hide my React Table or a column.
import "react-table/react-table.css";
import React, { Component, useState, useEffect } from "react";
function dataquery() {
return fetch("www.website.com").then(response =>
response.json()
);
}
function Offence() {
const [count, setCount] = useState([]);
useEffect(() => {
dataquery().then(headlines => {
setCount(headlines);
});
}, []);
//event.preventDefault();
console.log(count.offences);
let data = [{}];
if (typeof count.offences !== "undefined" ) {
let newdata = count.offences.map(count => data.push({ name: count }));
// console.log("???");
}
console.log(typeof count.offence);
const columns = [
{
Header: "Name",
accessor: "name",
},
];
// trying to hide this react table or just hide the column
return <ReactTable data={data} columns={columns} filterable />;
}
export default Offence;
I am exporting this function into another file in which I render 'Offence'.
The fetch website is not the actual website as it needs a private login to work, so I replaced it with this placeholder for the purpose of this.
Thank you.

You can easily add an useState hook, which stores a boolean to show/not show the table. Then toggle this state when changing a checkbox. For example:
function Offence() {
// ... your component code
const [showTable, setShowTable] = useState(true);
// output
return (
<>
<input
type="checkbox"
checked={showTable}
onChange={() => { setShowTable(p => !p); }}
/>
{showTable ? <ReactTable ... /> : null}
</>
);
}

I have only used classes and am new to using Functions
In such a situation, I highly recommend to use materialui components. It would make your life much easier because you need not to step into gory details of styling anymore if you do not want to.
Below I roughly drafted a component which contains a table and a list of checkboxes for switching hide/show columns.
Hope it helps :)
import React, { useState, useEffect } from 'react'
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardHeader from '#material-ui/core/CardHeader';
import CardContent from '#material-ui/core/CardContent';
import Table from '#material-ui/core/Table';
import TableBody from '#material-ui/core/TableBody';
import TableCell from '#material-ui/core/TableCell';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import FormControl from '#material-ui/core/FormControl';
import FormGroup from '#material-ui/core/FormGroup';
import FormControlLabel from '#material-ui/core/FormControlLabel'
import Checkbox from '#material-ui/core/Checkbox';
function App() {
const [showName, setshowName] = useState(true);
const [showQty, setshowQty] = useState(true);
const [showPrice, setshowPrice] = useState(true);
const dummydata = [{ name: "apple", qty: 12, price: 3.3 }, { name: "orange", qty: 3, price: 1.5 }, { name: "grape", qty: 10, price: 4.3 }]
return (
<Grid
container
direction="row"
justify="space-around"
alignItems="center"
>
<Grid item xs={12} sm={5}>
<Card>
<Table>
<TableHead>
<TableRow>
{showName ? <TableCell>Name</TableCell> : ""}
{showQty ? <TableCell >Qty.</TableCell> : ""}
{showPrice ? <TableCell >Price</TableCell> : ""}
</TableRow>
</TableHead>
<TableBody>
{dummydata.map(item => (
<TableRow>
{showName ? <TableCell component="th" scope="row" padding="none">
{item.name}
</TableCell> : ""}
{showQty ? <TableCell>{item.qty}</TableCell> : ""}
{showPrice ? <TableCell>{item.price}</TableCell> : ""}
</TableRow>
))}
</TableBody>
</Table>
</Card>
</Grid>
<Grid item xs={12} sm={5}>
<Card>
<CardHeader
title="Hide any column?"
/>
<CardContent>
<FormControl style={{ margin: 4 }}>
<FormGroup>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowName(checked)} checked={showName} />
}
label="Hide Name"
/>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowQty(checked)} checked={showQty} />
}
label="Hide Quantity"
/>
<FormControlLabel
control={
<Checkbox onChange={(e, checked) => setshowPrice(checked)} checked={showPrice} />
}
label="Hide Price"
/>
</FormGroup>
</FormControl>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
export default App;

Related

How to use map in react component using nextjs app?

I have data returned from the backend as an array that i want to populate on react component.
home.js
import Head from "next/head";
import Header from "../src/components/Header";
import * as React from 'react';
import { styled } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Paper from '#mui/material/Paper';
import Grid from '#mui/material/Grid';
import TextField from '#mui/material/TextField';
import SendIcon from '#mui/icons-material/Send';
import Stack from '#mui/material/Stack';
import Button from '#mui/material/Button';
import getDupImages from "../src/services/getDupImages";
import { useState, useEffect } from 'react'
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
export default function Home({data}) {
let _data;
const fetchData = async () => {
_data = await getDupImages();
console.log("DATA>>>", _data);
return _data;
};
const submit = (event) => {
event.preventDefault();
fetchData();
}
return (
<>
<Head>
<title>De-Dup</title>
<link rel="icon" type="image/ico" href="/img/goals.ico" />
</Head>
<Header />
<section>
<Box sx={{ flexGrow: 1 }}>
<Grid container spacing={2}>
<Grid item xs={5}>
<Box
component="form"
sx={{
'& > :not(style)': { m: 1, width: '75ch' },
}}
noValidate
autoComplete="off"
>
<TextField id="outlined-basic" label="location path" variant="outlined" />
<Stack direction="row" spacing={2}>
<Button variant="contained" onClick={submit} endIcon={<SendIcon />}>
Submit
</Button>
</Stack>
</Box>
</Grid>
<Grid item xs={7}>
{data.map((d) => {
return (
<div>
{d.title}
</div>
);
})}
</Grid>
</Grid>
</Box>
</section>
</>
);
}
Error
1 of 1 unhandled error
Server Error
TypeError: Cannot read property 'map' of undefined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Put the data in the component state and check if there actually is data before displaying it.
const [data, setData] = useState();
const fetchData = async () => {
setData(await getDupImages());
}
Then in your JSX:
{!!data && data.map(d => <div>{d.title}</div>}
You are trying to render the data before it is available. Use this instead
{data && data.map((d) => {
return (
<div>
{d.title}
</div>
);
})}
Either initialise the data state as an array or use the Optional chaining (?.) operator before the map function:
data?.map((d) => {
return <div>{d.title}</div>;
})
Hope this helps.

React - Secondary Prop Value - Node - Material UI

I've been working on simplifying my code and am curious how I would approach passing a secondary value using props and fetching data from the back end. I'm using material UI's Autocomplete and the PERN stack. Everything is working, except I want to change "region_name" to be a prop. So I can change the values dynamically within event details.js. when I'm fetching other data.
I currently have this component setup.
Production.js
import Autocomplete from "#mui/material/Autocomplete";
import Box from "#mui/material/Box";
import Stack from "#mui/material/Stack";
import TextField from "#mui/material/TextField";
export default function CustomAutoComplete(props) {
return (
<Stack sx={{ m: 1 }}>
<Autocomplete
sx={{ ml: 2, mr: 2 }}
size="small"
id="combo-box-demo"
freeSolo
inputValue={props.inputValue}
onInputChange={(event, newValue) => {
props.set(newValue);
}}
getOptionLabel={(data) => `${data.region_name}`}
options={props.data}
isOptionEqualToValue={(option, value) =>
option.region_name === value.region_name
}
renderOption={(props, data) => (
<Box component="li" {...props} key={data.id}>
{data.region_name}
</Box>
)}
renderInput={(params) => <TextField {...params} label="Region" />}
/>
</Stack>
);
}
Then importing it into a separate file EventDetails.js fetching the data and storing it in LocalStorage, which I'll move to useState eventually.
import CustomAutoComplete from "../../atoms/AutoComplete";
import FormGroup from "#mui/material/FormGroup";
import { Fragment, useState, useEffect } from "react";
import { useLocalStorage } from "../../utils/LocalStorage.js";
const EventDetails = () => {
const [region, setRegion] = useLocalStorage("region", "");
const [getRegion, setGetRegion] = useState([]);
// fetching backend data
useEffect(() => {
fetch("/authentication/region")
.then((response) => response.json())
.then((getRegion) => setGetRegion(getRegion));
}, []);
return (
<Fragment>
<FormGroup sx={{ p: 2, m: 1 }}>
<CustomAutoComplete
inputValue={region}
set={setRegion}
data={getRegion}
key={getRegion.id}
name={region_name} // <--feeble attempt
label="Region"
/>
</FormGroup>
</Fragment>
);
};
I had someone help me with finding a solution just had to create a prop variable like
export default function CustomAutoComplete(props) {
const { labelValue } = props;
return (renderOption={(props, data) => (
<Box component="li" {...props} key={data.id}>
{data[labelKey]}
</Box>
)})
then in the parent component
<CustomAutoComplete labelValue="region_name" />

Only display icon in parent if there's the relevant data in child component, but in order to have the data I must click on that icon

Alright, it took me awhile to even formulate the question for this. This is more related to a design strategy rather than anything.
I have a view that contains a table that displays a list of all users.Each row of the table has an icon on the left that will expand (or collapse) a component called UsersTrainingSummary that just brings a summary of some specific data. See image:
Here is the full code of this view component:
import React from 'react';
import { Card, CardContent, CardHeader, Chip, Divider, Grid, Typography } from '#material-ui/core';
import { gridSpacing } from '../../../store/constant';
import TableContainer from '#material-ui/core/TableContainer';
import Table from '#material-ui/core/Table';
import TableHead from '#material-ui/core/TableHead';
import TableRow from '#material-ui/core/TableRow';
import MuiTableCell from '#material-ui/core/TableCell';
import TableBody from '#material-ui/core/TableBody';
import { makeStyles, withStyles } from '#material-ui/core/styles';
import {Link} from "react-router-dom";
import IconButton from '#material-ui/core/IconButton';
import KeyboardArrowDownIcon from '#material-ui/icons/KeyboardArrowDown';
import KeyboardArrowUpIcon from '#material-ui/icons/KeyboardArrowUp';
import moment from 'moment';
import UsersTrainingSummary from './UsersTrainningSummary';
import useLocalStorage from 'react-use-localstorage';
import { useQuery } from '#apollo/client';
import ALL_USERS from '../../../graphql/AllUsers';
import Loader from '../../../component/Loader/Loader';
const useStyles = makeStyles({
table: {
minWidth: 350,
},
});
const TableCell = withStyles({
root: {
borderBottom: "none"
}
})(MuiTableCell);
function createData(userId, username, firstName, lastName, dateJoined, lastLogin, email, isActive, trainings ) {
return { userId, username, firstName, lastName, dateJoined, lastLogin, email,isActive, trainings };
}
const UserDashboard = () => {
const classes = useStyles();
const [storage, setStorage] = useLocalStorage('orgId');
const orgId = storage
const { data, error , loading} = useQuery(ALL_USERS, {
variables: {
orgId: Number(orgId)
},
});
function Row(props){
const { row } = props;
const [open, setOpen] = React.useState(false);
return (
<React.Fragment>
<TableRow key={row.userId}>
<TableCell>
<IconButton aria-label="expand row" size="small" onClick={() => setOpen(!open)}>
{open ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
</IconButton>
</TableCell>
<Link to={{pathname: `/users/${row?.userId}`, state: { user: row}}}
style={{ textDecoration: 'none' }}>
<TableCell
className={classes.root} hover={true}
component="th" scope="row">{row?.username}
</TableCell>
</Link>
<TableCell>{row.firstName}</TableCell>
<TableCell>{row.lastName}</TableCell>
<TableCell>{moment(row?.dateJoined).format('MM/DD/YYYY')}</TableCell>
<TableCell>{moment(row?.lastLogin).format('MM/DD/YYYY')}</TableCell>
<TableCell>{row?.email}</TableCell>
<TableCell>{row?.isActive? <React.Fragment>Yes</React.Fragment> : <React.Fragment>No</React.Fragment>}</TableCell>
</TableRow>
<TableRow>
<TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={6}>
{open && <UsersTrainingSummary userId={row.userId} trainings={row.trainings}/>}
</TableCell>
</TableRow>
</React.Fragment>
)
}
if(data) {
let userList = data.organization?.user
const rows = [];
if (userList) {
userList.map((user) => {
rows.push(createData(user.id, user.username, user.firstName, user.lastName, user.dateJoined, user.lastLogin,
user.email, user.isActive, user.trainings))
})
}
return (
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<Card>
<CardHeader
title={
<Typography component="div" className="card-header">
List of all trainees
</Typography>
}
/>
<Divider/>
<CardContent className="p-0">
<TableContainer>
<Table className={classes.table} aria-label="simple table">
<TableHead>
<TableRow>
<TableCell></TableCell>
<TableCell>Username</TableCell>
<TableCell>First Name</TableCell>
<TableCell>Last Name</TableCell>
<TableCell>Date Joined</TableCell>
<TableCell>Last Login</TableCell>
<TableCell>Email</TableCell>
<TableCell>Is Active</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows?.length > 0 && rows.map((row) => (
<Row key={row?.userId} row={row}/>
))}
</TableBody>
</Table>
</TableContainer>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
else
return ( <Loader />)
}
export default UserDashboard
Because the query that brings the summary data can be quite slow if you try to bring for too many users at once due to the involved tables in the DB having a huge amount of data, and because most of the time you will only need for one or a few users, I decided to use a lazy strategy: only when the user clicks on that arrow icon, the component UsersTrainingSummary is rendered and there I have the graphql query that brings the data from the backend and
also do all the necessary logic to render that Collapse component.
What I am trying to achieve now is to only display that icon for users that actually have something to show e.g query does not bring empty results from BE, because as user it feels a little misleading to me to have that icon just so you can click and see a message "oh...nothing here". However, since I need to execute the query first in order to know if there's something to display, it doesn't seem possible to me unless I perform that query first for all users, which is something I really don't wanna do, as the weight on performance is much bigger than the weight on user friendly interface.
Or, is there a way I can achieve this?
As mentioned in the comment, an alternative to querying the DB in order to find all users who have additional information, could be managing the issue on back-end side, by adding a boolean field (e.g. "hasMoreInfo") to the table that you use to populate the first view and use that value to decide whether to render or not the icon.

Reusable table component with Reactjs and material UI

I came from Vue.js Vuetify.js background.Vuetify.js has v-data-table component.
Simply we pass headers and items to generate a nice table.
<v-data-table
:headers="headers"
:items="desserts"
></v-data-table>
If we want to add a button, image, or something like that to a table cell
What we do is
<v-data-table :headers="headers" :items="items" >
<template v-slot:item.images="{ item }">
<v-img
v-if="item.images"
max-width="150px"
:src="item.images"
contain
></v-img>
</template>
<template v-slot:item.update="{ item }">
<v-btn
#click="
$router.replace({
path: '/create-product',
query: { id: item.id },
})
"
>
<v-icon>edit</v-icon>
</v-btn></template
>
</v-data-table>
This is very clean and easy.
In React.js also I could achieve the first thing using this
export default function ReusableTable({ headers, items }) {
return (
<Grid container>
<Grid item>
<Card>
<CardContent>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow>
{headers.map((header, i) => (
<TableCell key={i}>{header.text.toUpperCase()}</TableCell>
))}
</TableRow>
</TableHead>{' '}
<TableBody>
{items.map((item, i) => (
<TableRow key={i}>
{headers.map(({ value }) => (
<TableCell key={value}>{item[value]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</CardContent>
</Card>
</Grid>
</Grid>
);
}
Here also I pass the headers and items.
I want to display buttons, links, images, chips (UI) for certain columns in the table. How do I achieve that in the React world?
If I further explain, I want to pass items array (array of object). Object's imageSRC property should render with an img tag. Something like that.
Something like this should work. This is conditionally rendering an image tag if there is an item.images as stated in your question. Next it will render a Material Button if item.update exists. Alternatively, it simply renders the item[value].
Here is abbreviated code:
export default function ReusableTable({ headers, items }) {
const dynamicRender = (item, value)=>{
if(item && item.images){
return <img src=`${item.images}`/>
} else if(item && item.update){
return <Button href="/create-product">Link</Button>
} else {
return item[value];
}
}
return (
<TableBody>
{items.map((item, i) => (
<TableRow key={i}>
{headers.map(({ value }) => (
<TableCell key={value}>{dynamicRender(item, value)}</TableCell>
))}
</TableRow>
))}
</TableBody>
);
}
Try something like this
import React from "react";
import TableContainer from "#material-ui/core/TableContainer";
import Table from "#material-ui/core/Table";
import Paper from "#material-ui/core/Paper";
import TableHead from "#material-ui/core/TableHead";
import TableRow from "#material-ui/core/TableRow";
import TableCell from "#material-ui/core/TableCell";
import TableBody from "#material-ui/core/TableBody";
import { getSlots } from 'helpers/Slots'
const BaseTable = ({ headers, items, children, ...otherProps }) => {
const [actions] = getSlots(['actions'], children)
const tableConfig = {
size: "small",
}
const rowConfig = {
...otherProps,
}
const dynamicRenderer = (item, value) => {
if (value === 'actions') {
return children(item)
} else {
return item[value]
}
}
return (
<>
<TableContainer component={Paper}>
<Table {...tableConfig}>
<TableHead>
<TableRow {...rowConfig}>
{headers.map((header, i) => (
<TableCell key={i}>{header.label}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{items.map((item, i) => (
<TableRow key={i} {...rowConfig}>
{headers.map(({ value }) => (
<TableCell key={value}>{dynamicRenderer(item, value)}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}
export default BaseTable
Then added helpers
import React from "react";
const Slot = ({ children }) => <>{children}</>
const getSlots = ( names, children) => {
return names.map(name => {
let slot = null;
React.Children.forEach(children, child => {
if (!React.isValidElement(child)) {
return;
}
if (child.type === Slot && (child.props).name === name) {
slot = React.cloneElement(child);
}
});
return slot;
});
}
export { Slot, getSlots }
import React, { useState, useEffect } from "react"
import Grid from '#material-ui/core/Grid'
import BaseTable from 'helpers/Table'
import { Slot } from 'helpers/Slots'
import PencilBoxOutline from 'mdi-react/PencilBoxIcon'
import DeleteOutline from 'mdi-react/DeleteIcon'
const headers = [
{value: 'name', label: 'Name'},
{value: 'age', label: 'Age'},
{value: 'gender', label: 'Gender'},
{value: 'registeredDate', label: 'Date of Registration'},
{value: 'requirements', label: 'Semester Requirements'},
{value: 'percentage', label: 'Percentage (%)'},
{value: 'actions', label: 'Actions'},
]
const items = [
{
id: 1,
requirements: 'Pay at least 50% ',
percentage: '10%',
name: "John Doe",
age: 30,
registeredDate: "2021/10/30",
gender: "Male"
},
{
id: 2,
requirements: 'Just go with it',
percentage: '50%',
name: "Jane Doe",
age: 40,
registeredDate: "2021/10/30",
gender: "Female"
},
]
const Test = () => {
return (
<Grid container spacing={4}>
<Grid item xs={12}>
<Grid container justifyContent="space-between" spacing={2}>
<Grid item></Grid>
<Grid item sm={9}></Grid>
</Grid>
<BaseTable headers={headers} items={items}>
{(item) => (
<Slot name="actions">
<PencilBoxOutline onClick={(item) => onOpenDialog(item)}/>
<DeleteOutline onClick={(item) => onDelete(item)} />
</Slot>
)}
</BaseTable>
</Grid>
</Grid>
)
}
export default Test

Show Checkboxes on all TableRow elements once one is checked

I have a material-ui Table and I'm adding multi-select capability to it. My requirements are as follows.
Checkboxes are all initially hidden. - DONE
When hovering on a row its checkbox appears. DONE
Once one Checkbox is checked then all Checkbox elements on other rows become constantly visible.
I have the first two requirements done but I'm struggling with the final one.
How do I get all checkboxes to be visible (by changing opacity) once any one of them becomes checked ?
This is where I've got to so far :
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/core/styles";
import Table from "#material-ui/core/Table";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableHead from "#material-ui/core/TableHead";
import TableRow from "#material-ui/core/TableRow";
import Paper from "#material-ui/core/Paper";
import Checkbox from "#material-ui/core/Checkbox";
const useStyles = makeStyles({
rowIconStyle: {
minWidth: 50,
minHeight: 50
},
tableRowStyle: {
cursor: "pointer",
"&:hover": {
backgroundColor: "darkGrey"
}
},
rowSelectionCheckboxStyle: {
//opacity: "calc(var(--oneRowSelected))",
opacity: 0,
"$tableRowStyle:hover &": {
opacity: 1
}
}
});
export default function MyTableComponent(props) {
const styles = useStyles();
const DEFAULT_TABLE_ROW_ICON_COLOR = "grey";
return (
<TableContainer component={Paper}>
<Table aria-label="simple table">
<TableHead>
<TableRow>
<TableCell>
<Checkbox className={styles.rowSelectionCheckboxStyle} />
</TableCell>
<TableCell>Icon</TableCell>
<TableCell>Name</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.tableRowsData.map(row => {
const RowIcon =
row.icon && row.icon.iconElement
? row.icon.iconElement
: () => <div />;
let iconElement = (
<RowIcon
className={styles.rowIconStyle}
style={{
color:
row.icon && row.icon.color
? row.icon.color
: DEFAULT_TABLE_ROW_ICON_COLOR
}}
/>
);
return (
<TableRow key={row.name} className={styles.tableRowStyle}>
<TableCell>
<Checkbox className={styles.rowSelectionCheckboxStyle} />
</TableCell>
<TableCell>{iconElement}</TableCell>
<TableCell>{row.projectName}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
);
}
MyTableComponent.propTypes = {
tableRowsData: PropTypes.array
};
Have a value and an onChange in your Checkbox. Maintain a state which represents the selected checkboxes and update it upon onChange. Check the checkboxes array and only attach the class styles.rowSelectionCheckboxStyle if any one checkbox is checked.
Working copy of your code is here
export default function MyTableComponent(props) {
const [checkedRows, setCheckedRows] = useState({});
const styles = useStyles();
const DEFAULT_TABLE_ROW_ICON_COLOR = "grey";
const anyChecked = () => {
let anyRowChecked = false;
for (let key in checkedRows) {
if (checkedRows[key]) {
anyRowChecked = true;
}
}
return anyRowChecked;
};
const checked = (checked, index) => {
setCheckedRows(prev => ({ ...prev, [index]: checked }));
};
...
...
return (
<TableRow key={row.name} className={styles.tableRowStyle}>
<TableCell>
<Checkbox
checked={checkedRows[index]}
onChange={e => checked(e.target.checked, index)}
className={
!anyChecked() && styles.rowSelectionCheckboxStyle
}
/>
</TableCell>
<TableCell>{iconElement}</TableCell>
<TableCell>
{checkedRows[index] ? "True" : "False"} {row.projectName}
</TableCell>
</TableRow>
);
})}
...

Categories