I have an issue, I need to show and hide DropDown/Selects field based on Checkbox's state. Means when checkbox state is checked I need to show Dropdown and when I uncheck it should hide. I have attached a code of this component below. As you can see I have 3 Dropdowns created already, but I need to hide/show them by checking/unchecking one single checkbox
import React, { useState, useEffect } from "react";
import { Button } from "../../components/Button";
import { Checkbox } from "../../components/Checkbox";
import { FormBuilder } from "../../components/FormBuilder";
import { Grid, GridCell, GridRow } from "../../components/Grid";
import { LinearProgress } from "../../components/LinearProgress";
import { Select } from "../../components/Select";
import { TextField } from "../../components/TextField";
import { Name } from "../../models/Name";
import { Option } from "../../models/Option";
import { DynamicForm } from "../../models/DynamicForm";
import "./index.css";
import { Organization } from "../../models/Organization";
import { request } from "../../api";
import { serialize } from "class-transformer";
import { CodingTemplate } from "../../models/CodingTemplate";
import { useHistory } from "react-router-dom";
import { useRef } from "react";
const NUMBER_OF_STEPS = 3;
const screen = {
first: 1,
second: 2,
third: 3,
};
const FormDetails = ({ dynamicForm }: { dynamicForm: DynamicForm }) => {
const check1 = useRef();
return (
<div>
<h4 style={{ margin: 16 }}>Данные формы</h4>
<Grid>
<GridRow>
<GridCell span={5}>
<Checkbox
label="CHECKBOX"
onChange={(value) => (dynamicForm.needExtraApprove = value)}
defaultChecked={dynamicForm.needExtraApprove}
/>
</GridCell>
</GridRow>
<GridRow>
<GridCell span={4}>
<Select<Option>
required
//defaultValue={dynamicForm.departmentHash}
label="Select to show/hide"
type={Option}
//api="/state_schedule/departments-with-journals/"
defaultOptions={defaultOptions}
//onChange={(value: Option) => dynamicForm.departmentHash = value}
/>
}
</GridCell>
<GridCell span={4}>
<Select<Option>
required
//defaultValue={dynamicForm.departmentHash}
label="Select to show/hide"
type={Option}
//api="/state_schedule/departments-with-journals/"
defaultOptions={defaultOptions}
//onChange={(value: Option) => dynamicForm.departmentHash = value}
/>
</GridCell>
<GridCell span={4}>
<Select<Option>
required
//defaultValue={dynamicForm.departmentHash}
label="Select to show/hide"
type={Option}
//api="/state_schedule/departments-with-journals/"
defaultOptions={defaultOptions}
//onChange={(value: Option) => dynamicForm.departmentHash = value}
/>
</GridCell>
</GridRow>
</Grid>
</div>
);
};
const FormFields = ({ dynamicForm }: { dynamicForm: DynamicForm }) => (
<div>
<h4 style={{ margin: 16 }}>ASD</h4>
<FormBuilder dynamicForm={dynamicForm} />
</div>
);
export default () => {
const [step, setStep] = useState(1);
const [dynamicForm, setDynamicForm] = useState<DynamicForm>(
new DynamicForm()
);
let progress = parseFloat(((step - 1) * (1 / NUMBER_OF_STEPS)).toFixed(2));
const onBackButtonPress = () => {
if (step > 0) {
setStep((prev) => prev - 1);
}
};
const onNextButtonPress = () => {
event.preventDefault();
if (step < screen.third) {
setStep((prev) => prev + 1);
} else {
console.log("submit!");
//submit
}
// if (step < screen.third) {
// setStep((prev) => prev + 1);
// } else {
// console.log("submit!");
// //submit
// }
};
const history = useHistory();
const onSubmitTest = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
console.log(dynamicForm);
await request("/service-desk/forms/", {
method: "POST",
body: serialize(dynamicForm),
});
history.push({
pathname: "/service-desk/application-form",
});
} catch (error) {
console.log(error);
alert("ERROR");
}
};
let content = <SubDivision dynamicForm={dynamicForm} />;
let nextBtnLabel = "NEXT";
if (step === screen.second) {
content = <FormDetails dynamicForm={dynamicForm} />;
} else if (step === screen.third) {
content = <FormFields dynamicForm={dynamicForm} />;
}
return (
<form onSubmit={onSubmitTest} noValidate={step === screen.third}>
<LinearProgress progress={progress} determinate />
{content}
<div className="request-btn-container">
<Button
label="BACK"
disabled={step == 1}
onClick={onBackButtonPress}
/>
{step === 3 ? (
<Button label="SAVE" raised type="submit" />
) : (
<Button
label="NEXT"
disabled={step == 3}
onClick={onNextButtonPress}
/>
)}
</div>
</form>
);
};
. Thank you for any tip or advice!
Let me know if this helps.
const FormDetails = ({ dynamicForm }: { dynamicForm: DynamicForm }) => {
const check1 = useRef();
return (
<div>
<h4 style={{ margin: 16 }}>Данные формы</h4>
<Grid>
<GridRow>
<GridCell span={5}>
<Checkbox
label="CHECKBOX"
onChange={(value) => (dynamicForm.needExtraApprove = value)}
defaultChecked={dynamicForm.needExtraApprove}
/>
</GridCell>
</GridRow>
{dynamicForm.needExtraApprove ? (
<GridRow>
<GridCell span={4}>
// put your select here
</GridCell>
<GridCell span={4}>
// put your select here
</GridCell>
<GridCell span={4}>
// put your select here
</GridCell>
</GridRow>
): null}
</Grid>
</div>
);
};
Related
I have a react-kendo datagrid and its cells are selectable. There are 385 rows and each row has 4 Selectable cells.
The problem is; When any of the checkboxes is checked, the whole grid is rerender. Accordingly, it takes about 3 seconds to be checked. Is it possible to regenerate only the edited cell?
import { useDispatch } from "react-redux";
import { setScopes } from "../../redux/actions/scopeActions";
import {
Grid,
GridColumn as Column,
GridToolbar,
} from "#progress/kendo-react-grid";
import { filterBy } from "#progress/kendo-data-query";
import "#progress/kendo-theme-material/dist/all.css";
import { Checkbox } from "#material-ui/core";
function NewApiScopesTable({ scopes }) {
const dispatch = useDispatch();
const [rows, setRows] = useState(scopes);
// const [filter, setFilter] = useState();
// const filterChange = (event) => {
// setData(filterBy(scopes, event.filter));
// setFilter(event.filter);
// };
useEffect(() => {
setRows(scopes);
}, [scopes]);
useEffect(() => {
const selectedRows = rows.filter(
(row) => !!row.get || !!row.post || !!row.put || !!row.delete
);
dispatch(setScopes(selectedRows));
}, [rows]);
const handleSelect = (row) => {
const newRows = [...rows];
const index = newRows.findIndex((r) => r.id === row.id);
newRows[index] = { ...row };
setRows(newRows);
};
const getCell = (props) => {
return (
<td>
<Checkbox
inputProps={{ "aria-label": "primary checkbox" }}
checked={props.dataItem.get}
onChange={() =>
handleSelect({ ...props.dataItem, get: !props.dataItem.get })
}
/>
</td>
);
};
const postCell = (props) => {
if (!props.dataItem.isPostEnable) {
return <td></td>;
} else {
return (
<td>
<Checkbox
inputProps={{ "aria-label": "primary checkbox" }}
checked={props.dataItem.post}
onChange={() =>
handleSelect({ ...props.dataItem, post: !props.dataItem.post })
}
/>
</td>
);
}
};
const putCell = (props) => {
if (!props.dataItem.isPutEnable) {
return <td></td>;
} else {
return (
<td>
<Checkbox
inputProps={{ "aria-label": "primary checkbox" }}
checked={props.dataItem.put}
onChange={() =>
handleSelect({ ...props.dataItem, put: !props.dataItem.put })
}
/>
</td>
);
}
};
const deleteCell = (props) => {
if (!props.dataItem.isDeleteEnable) {
return <td></td>;
} else {
return (
<td>
<Checkbox
inputProps={{ "aria-label": "primary checkbox" }}
checked={props.dataItem.delete}
onChange={() =>
handleSelect({
...props.dataItem,
delete: !props.dataItem.delete,
})
}
/>
</td>
);
}
};
console.log("run");
return (
<Grid
style={{
height: "500px",
}}
data={rows}
// filter={filter}
// onFilterChange={filterChange}
>
<GridToolbar>
<input type="search" id="site-search" name="q" onChange />
</GridToolbar>
<Column field="name" title="Endpoint Name" width="400px" />
<Column field="get" title="GET" cell={getCell} />
<Column field="post" title="POST" cell={postCell} />
<Column field="put" title="PUT" cell={putCell} />
<Column field="delete" title="DELETE" cell={deleteCell} />
</Grid>
);
}
export const MemoizedNewApiScopesTable = React.memo(NewApiScopesTable);
Old question but for anyone stumbling upon this later, what worked for me was to memoize any custom cells. (not sure if this will fix your particular performance issue or now but it should help)
const handleSelect = useCallback((row) => {
const newRows = [...rows];
const index = newRows.findIndex((r) => r.id === row.id);
newRows[index] = { ...row };
setRows(newRows);
}, [//dependencies here]);
const getCell = useMemo((props) => {
return (
<td>
<Checkbox
inputProps={{ "aria-label": "primary checkbox" }}
checked={props.dataItem.get}
onChange={() =>
handleSelect({ ...props.dataItem, get: !props.dataItem.get })
}
/>
</td>
);
},[handleSelect, props.dataItem]);
Notice the useMemo and useCallback hooks added. (apply any dependencies to make the linter happy, I only added a few)
The useCallback is needed on any function you put in the dependency array of useMemo, otherwise it will run every render.
Follow this patter for all custom cells and see if it helps performance.
I hope you could help me with this part, so I have no problem with the stepper and no problem with the forms too when it comes to getting data input and validating fields with yup .
but I have a problem with the checkboxes and autocomplete. when I go back to a previous step, I lose the values I have already entered and validation no longer works with checkbox and autocomplete field.
There is an example of a hook that I made for the checkboxes that I will use later in a form :
const CheckBoxField = (props) => {
const { label, ...rest } = props;
const [field, meta, helper] = useField(props);
const { setValue } = helper;
const [touched, error] = at(meta, "touched", "error");
const isError = error && touched && true;
function __renderHelperText() {
if (isError) {
return <FormHelperText>{error}</FormHelperText>;
}
}
function _onChange(event) {
setValue(event.target.checked);
}
const configFormControl = {
...field,
...rest,
onChange: _onChange,
};
return (
<FormControl
component="fieldset"
{...configFormControl}
error={Boolean(isError)}
>
<FormControlLabel
// value={field.checked}
checked={field.checked}
label={label}
onChange={_onChange}
control={
<BpCheckbox
{...configFormControl}
// checked={field.checked}
color="primary"
/>
}
color="primary"
/>
{__renderHelperText()}
</FormControl>
);
};
And there is the code for validation :
import * as yup from "yup";
import signUpFormModel from "../signUpFormModel";
const {
formField: {
terms //Boolean , used in checkboxes, should be true
},
} = signUpFormModel;
const signUpValidationSchema = [
yup.object().shape({
[terms.name]: yup.boolean().oneOf([true], `${terms.requiredErrMsg}`),
//other fields ...
}),
//other forms ...
];
export default signUpValidationSchema;
My Initial value :
import signUpFormModel from "../signUpFormModel";
const {
formField: {
terms,
},
} = signUpFormModel;
const formInitialValue = {
[terms.name]: false,
};
export default formInitialValue;
and some props helper (used as model for my forms)
import React, { Fragment } from "react";
import { Link } from "#mui/material";
const signUpFormModel = {
formId: "registration",
formField: {
terms: {
type: "checkbox",
name: "terms",
id: "terms",
label: () => (
<Fragment>
J'accepte les{" "}
<Link href="#" variant="body2">
termes et conditions générales d'utilisation
</Link>{" "}
et la{" "}
<Link href="#" variant="body2">
politique de confidentialité
</Link>
.
</Fragment>
),
requiredErrMsg: "Vous devez accepter les termes et conditions",
},
},
};
export default signUpFormModel;
Finaly the form itself :
import React from "react";
import { Grid } from "#mui/material";
import CheckBoxField from "../CheckBoxField";
const PrimarySignUpForm = (props) => {
const {
formField: {
terms,
},
} = props;
return (
<Grid container spacing={2}>
<Grid item xs={12}>
<CheckBoxField name={terms.name} label={terms.label()} />
</Grid>
</Grid>
);
};
export default PrimarySignUpForm;
And this is how I made the stepper :
import React, { Fragment, useState } from "react";
import SignUpFormLogs from "../forms/SignUpFormLogs";
import { Formik, Form } from "formik";
import formInitialValuefrom from "../formModel/formInitialValue";
import signUpFormModel from "../formModel/signUpFormModel";
import signUpValidationSchema from "../formModel/signUpValidationSchema";
import {
Button,
Link,
Step,
StepLabel,
Stepper,
Typography,
} from "#mui/material";
import { Box } from "#mui/system";
const steps = ["step1"];
const { formId, formField } = signUpFormModel;
function _renderSteps(step) {
switch (step) {
case "step1":
return <SignUpFormLogs formField={formField} />;
default:
return <div>Not Found</div>;
}
}
function _sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const SignUp = () => {
const [activeStep, setActiveStep] = useState(0);
const currentValidationSchema = signUpValidationSchema[activeStep]; //activeStep
const isLastStep = activeStep === steps.length - 1;
async function _submitForm(values, actions) {
await _sleep(1000);
console.log(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
setActiveStep(activeStep + 1);
}
function _handleSubmit(values, actions) {
if (isLastStep) {
_submitForm(values, actions);
} else {
setActiveStep(activeStep + 1);
actions.setTouched({});
actions.setSubmitting(false);
}
}
function _handleBack() {
setActiveStep(activeStep - 1);
}
return (
<Fragment>
<Stepper activeStep={activeStep} sx={{ pt: 3, pb: 5 }}>
{steps.map((label) => (
<Step key={label}>
<StepLabel>{label}</StepLabel>
</Step>
))}
</Stepper>
{activeStep === steps.length ? (
<Typography variant="h6">Last step</Typography>
) : (
<Formik
initialValues={formInitialValuefrom}
validationSchema={currentValidationSchema}
onSubmit={_handleSubmit}
>
{({ isSubmitting }) => (
<Form id={formId}>
{_renderSteps(steps[activeStep])}
{activeStep !== 0 && (
<Button
onClick={_handleBack}
disabled={isSubmitting}
variant="outlined"
color="primary"
sx={{ mt: 4, mb: 2 }}
>
Back
</Button>
)}
<Button
type="submit"
disabled={isSubmitting}
variant="contained"
color="primary"
sx={{ mt: 4, mb: 2 }}
>
{isLastStep ? "Enregistrer" : "Suivant"}
</Button>
{isSubmitting && (
<Typography variant="h6">submitting...</Typography>
)}
</Form>
)}
</Formik>
)}
</Fragment>
);
};
export default SignUp;
This seems like it should be easy but why isn't a button callback function with a setState call not triggering a refresh of the data item? Actually it's just the computeSMA button that isn't changing the sma when the button is selected. The other two callbacks to set inputs work. The fetchData updates the charts so i can't figure this out!! Must be too tired ...
import React, { useState, useEffect } from "react"
import { useRecoilState } from "recoil";
import { closingDataAtom, metaDataAtom, tickerAtom, timeSeriesAtom , smaAtom} from '../../utils/atoms'
import { Container } from '#material-ui/core'
import '../../utils/Home.css'
import { VictoryChart, VictoryBar, VictoryTheme, VictoryVoronoiContainer, VictoryLine, VictoryBrushContainer, VictoryZoomContainer } from 'victory';
import { Chart, Axis, Tooltip, Line, Point } from "bizcharts";
import {XYPlot, LineSeries} from 'react-vis';
const APIKEY = 'demo'
const Home = () => {
const [apikey, setApiKey] = useState(APIKEY)
const [ticker, setTicker] = useRecoilState(tickerAtom);
const [metadata, setMetaData] = useRecoilState(metaDataAtom)
const [closingdata, setClosingData] = useRecoilState(closingDataAtom)
const [dates, setDates] = useRecoilState(timeSeriesAtom)
const [sma, setSMA] = useRecoilState(smaAtom)
const TIME_RESOLUTION = 'Daily'
var requestUrl
if (TIME_RESOLUTION === 'Daily') {
requestUrl = "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=" + ticker + "&outputsize=full&apikey=" + apikey
} else {
requestUrl = "https://www.alphavantage.co/query?function=TIME_SERIES_WEEKLY_ADJUSTED&symbol=" + ticker + "&outputsize=full&apikey=" + apikey;
}
const fetchData = async () => {
fetch(requestUrl)
.then(response => response.json())
.then(data => {
var closing_price = []
var metadata = []
var dat = []
Object.keys(data['Time Series (Daily)']).forEach((dateKey) => {
closing_price.push(data['Time Series (Daily)'][dateKey]['5. adjusted close'])
dat.push({ 'date': new Date(dateKey) })
})
Object.keys(data['Meta Data']).forEach((metaKey) => {
metadata.push(data['Meta Data'][metaKey])
})
setDates(dat.reverse())
setClosingData(closing_price.reverse())
setMetaData(metadata)
})
.catch(e => {
});
};
const handleSMACompute = (e) => {
var sm = ['2', '3', '4']
setSMA(sm) <====== REACT IS NOT "REACTING"
}
const handleTickerInput = (e) => {
setTicker(e.target.value)
}
const handleAPIInput = (e) => {
setApiKey(e.target.value)
}
return (
<>
<Container className="container" maxWidth="sm">
<div>
<label>Ticker:</label> {ticker}
<input type="text" name="ticker" onChange={handleTickerInput} />
</div>
<div>
<label>APIKEY:</label> {apikey}
<input type="text" name="apikey" onChange={handleAPIInput} />
</div>
<button onClick={fetchData}>
Click it
</button>
<Container className="container" maxWidth="sm">
<ul>{metadata}</ul>
</Container>
<button OnClick={handleSMACompute}> Generate SMA </button>
<Container className="container" maxWidth="sm">
<ul>The value is {sma}</ul>
</Container><div>
</div>
<VictoryChart
theme={VictoryTheme.material}
domainPadding={10}
>
<VictoryBar
style={{ data: { fill: "#c43a31" } }}
data={closingdata}
/>
</VictoryChart>
<div>
<VictoryChart
theme={VictoryTheme.material}
>
<VictoryLine
style={{
data: { stroke: "#c43a31" },
parent: { border: "1px solid #ccc" }
}}
animate={{
duration: 20,
onLoad: { duration: 20 }
}}
containerComponent={<VictoryZoomContainer zoomDomain={{x: [5, 35], y: [0, 100]}}/>}
categories={{
y: dates
}}
data={closingdata}
/>
</VictoryChart>
</div>
</Container>
</>
);
}```
It seems to have been the button setup. I changed to this and it works....??ggrrrr
Click it
</button>
<Container className="container" maxWidth="sm">
<li>{metadata}</li>
</Container>
<button onClick={computeSMA}>
Click it
</button>
<Container className="container" maxWidth="sm">
<li>{sma}</li>
</Container>
In your first code, you used OnClick as the event name. Should be onClick. It is a react syntax and it is case sensitive.
Hello everyone :D I need your advise/tip. Right now I have a APIDataTable component. It has its rows, columns and etc. This component is responsible to show/build data table on frontend with search bar in it above the table. I have an search bar, which is not functional right now. I want it to search data from data table. What should I start from? How can i make it perform search in Table. Thank you for any advise and tip <3
import React, { useEffect, useState } from "react";
import { plainToClassFromExist } from "class-transformer";
import { Pagination } from "../../models/Pagination";
import {
DataTable,
DataTableHead,
DataTableHeadCell,
DataTableBody,
DataTableRow,
DataTableCell,
} from "../DataTable";
import { request } from "../../api";
import "./index.css";
import { MenuSurface } from "../MenuSurface";
import { IconButton } from "../IconButton";
import { Checkbox } from "../Checkbox";
import { Dialog } from "../Dialog";
import { GridCell, GridRow } from "../Grid";
import { Button } from "../Button";
export class Column<T> {
label: string;
width?: number;
filter?: JSX.Element;
render: (row: T) => JSX.Element | string;
constructor(column: Partial<Column<T>>) {
Object.assign(this, column);
}
}
type APIDataTableProps<T> = {
apiPath?: string;
params?: string;
columns?: Column<T>[];
type: Function;
onRowClick?: (row: T) => void;
};
export const APIDataTable = <T extends object>({
apiPath,
params,
columns,
type,
onRowClick,
}: APIDataTableProps<T>) => {
const [data, setData] = useState<Pagination<T>>(null);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(15);
const [isLoading, setIsLoading] = useState(false);
const [isDialogOpen, setDialogOpen] = useState(false);
const [isMenuSurFaceOpen, setMenuSurfaceOpen] = useState(false);
const [hiddenColumns, setHiddenColumns] = useState<number[]>(
JSON.parse(localStorage.getItem(`hiddenColumns-${apiPath + params}`)) || []
);
const fetchData = async () => {
const urlSearchParams = new URLSearchParams(params);
urlSearchParams.set("page", page.toString());
urlSearchParams.set("page_size", pageSize.toString());
const url = `${apiPath}?${urlSearchParams}`;
const response = await request(url);
const data = plainToClassFromExist(new Pagination<T>(type), response, {
excludeExtraneousValues: true,
});
setData(data);
setIsLoading(false);
};
useEffect(() => {
if (!!apiPath) {
setIsLoading(true);
fetchData();
}
}, [page, pageSize]);
const headCells = columns
.filter((e, i) => !hiddenColumns?.includes(i))
.map((column) => (
<DataTableHeadCell key={column.label} width={column.width}>
{column.label}
</DataTableHeadCell>
));
const rows = data?.results?.map((row, index) => (
<DataTableRow
key={"row-" + index}
onClick={() => !!onRowClick && onRowClick(row)}
>
{columns
.filter((e, i) => !hiddenColumns?.includes(i))
.map((column) => {
return (
<DataTableCell key={column.label} width={column.width}>
<div className="data-table-cell-text">{column.render(row)}</div>
</DataTableCell>
);
})}
</DataTableRow>
));
let uncheckedCheckboxes = hiddenColumns;
const onCheckboxChange = (index: number, value: boolean) => {
if (!value) {
uncheckedCheckboxes.push(index);
//setHiddenColumns(uncheckedCheckboxes);
} else {
const array = [...uncheckedCheckboxes];
array.splice(array.indexOf(index), 1);
uncheckedCheckboxes = array;
}
};
const [isOpen, setIsOpen] = useState(false);
return (
<div className="data-table-container">
<div className="search-test">
<div className="mdc-menu-surface--anchor">
<label
className="mdc-text-field mdc-text-field--filled mdc-text-field--no-label mdc-text-field--with-leading-icon mdc-text-field--with-trailing-icon"
htmlFor="input"
id="search-menu-surface"
>
<IconButton density={-1} icon="search" />
<input
className="mdc-text-field__input "
type="text"
placeholder="Поиск"
id="searchinput"
/>
<IconButton
density={-1}
icon="arrow_drop_down"
onClick={() => {
setMenuSurfaceOpen(true);
}}
/>
</label>
<MenuSurface
isOpen={isMenuSurFaceOpen}
onClose={() => setMenuSurfaceOpen(false)}
fullwidth
>
<div className="data-table-filters-container">
{columns.map(
(column) =>
!!column.filter && (
<div className="data-table-filter">
<div className="data-table-filter-label mdc-typography--subtitle1">
{column.label}
</div>
<div className="data-table-column-filter">
{column.filter}
</div>
</div>
// <GridRow>
// <GridCell span={3}>{column.label}</GridCell>
// <GridCell span={3}>{column.filter}</GridCell>
// </GridRow>
)
)}
{/* <GridCell span={2}> */}
{/* <Button label="Поиск" raised /> */}
{/* <Button
label="Отмена"
raised
onClick={() => {
setIsOpen(false);
}}
/> */}
{/* </GridCell> */}
</div>
</MenuSurface>
</div>
<IconButton
onClick={() => {
setDialogOpen(true);
}}
density={-1}
icon="settings"
/>
<Dialog
isOpen={isDialogOpen}
onOkClick={() => {
localStorage.setItem(
`hiddenColumns-${apiPath + params}`,
JSON.stringify(uncheckedCheckboxes)
);
setDialogOpen(false);
setHiddenColumns(uncheckedCheckboxes);
}}
onCloseClick={() => setDialogOpen(false)}
>
<div
style={{
display: "flex",
flexDirection: "column",
}}
>
{columns.map((column, index) => (
<Checkbox
label={column.label}
onChange={(value) => onCheckboxChange(index, value)}
defaultChecked={!uncheckedCheckboxes.includes(index)}
/>
))}
</div>
</Dialog>
</div>
<DataTable
pagination={true}
count={data?.count}
rowsNumber={data?.results.length}
page={page}
next={data?.next}
previous={data?.previous}
isLoading={isLoading}
onNextClick={() => setPage(page + 1)}
onPreviosClick={() => setPage(page - 1)}
>
<DataTableHead>{headCells}</DataTableHead>
<DataTableBody>{rows}</DataTableBody>
</DataTable>
</div>
);
};
I'm guessing that you want to search bar to effectively filter out rows that don't match. in this case what you want to do is add a filter to the search text (naturally you'll add a state for the search value, but it looks like you'll have that handled.
You'll add your filter here const rows = data?.results?.filter(...).map
You filter function will look something like this
const rows = data?.results.filter((row) => {
// In my own code if I have other filters I just make them return false
// if they don't match
if (
searchText &&
!(
// exact match example
row.field === searchText ||
// case-insensitive example
row.otherField?.toLowerCase().includes(searchText)
// can continue with '||' and matching any other field you want to search by
)
)
return false;
return true;
}).map(...)
I am new to react, I am working on a project. I need little help regarding the filtering of the data. There are two filters one-: State and Location. The location is dependent on State, which is working fine, but after that, I want to filter my card component i.e VideoCategory card basis on above-selected value. There are three levels of filtering and for the third level, the filtering is not working.
Please, anyone, help me out, as I am trying to fix this issue from last two days.
import React, { useState, useEffect } from "react";
import Menu from "../common/Menu";
import { Form, Container, Row, Col } from "react-bootstrap";
import {
getVideosBasedOnCategories,
getAllStates,
} from "./helper/userApiCalls";
import VideoCard from "../common/VideoCard";
const CategoryVideos = ({ match }) => {
const [videoCategory, setVideoCategory] = useState([]);
const [selectState, setSelectState] = useState([]);
const [selectLocation, setSelectLocation] = useState([]);
let location="";
const preload = (categoryId) => {
getVideosBasedOnCategories(categoryId).then((data) => {
if (data.error || !data) {
console.log(data.error);
return <h1>No Data to Show Now</h1>;
} else {
setVideoCategory(...videoCategory, data);
}
});
};
//Intial Loading
useEffect(() => {
preload(match.params.categoryId);
getAllStateForSelect();
}, []);
//getting data for first Select Component from API
const getAllStateForSelect = () => {
getAllStates().then((data) => {
console.log(data);
if (data.error) {
return console.log(data.error);
} else {
setSelectState(...selectState, data);
}
});
};
const handleChange = (event) => {
console.log(event.target.value);
setSelectLocation(event.target.value);
};
const onLocationChange=(event)=>{
location = event
console.log(location)
}
//Storing Location into Option for Second Select
const onSplit = (x) => {
var arr = [];
for (let i = 0; i < x.citynames.length; i++) {
arr.push(
<option key={i} value={x.citynames[i]}>
{x.citynames[i]}
</option>
);
}
return arr;
};
return (
<div>
<Menu />
<Container style={{ marginTop: "200px" }}>
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select State</Form.Label>
<Form.Control
as="select"
onChange={handleChange.bind(selectState[0])}
custom
>
{selectState.map((data, index) => (
<option name="setmap" key={index} value={data._id}>
{data.statename}
</option>
))}
</Form.Control>
</Form.Group>
</Form>
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select Location</Form.Label>
<Form.Control
as="select"
onChange={(e) => onLocationChange(e.target.value)}
custom
>
{selectState
.filter((selectState) => selectState._id.includes(selectLocation))
.map((e) => onSplit(e))}
</Form.Control>
</Form.Group>
</Form>
<Row>
{videoCategory.filter(videocard=>videocard.location.includes(location.toString()))
.map((videocard) => {
return (
<Col lg={4} xs={12} md={12} className="py-3 px-3">
<VideoCard videoCategory={videocard}
/>
</Col>
);
})}
</Row>
</Container>
</div>
);
};
export default CategoryVideos;
VideoCategory File
import React, { Fragment,useEffect,useState } from "react";
import { Card, Button, Container, Row, Col } from "react-bootstrap";
import {getStateById} from '../user/helper/userApiCalls'
const VideoCard = ({ videoCategory }) => {
const [state,setState] = useState("");
const [city,setCity] = useState("");
//const { name, link, description } = videoCategory;
const getStateByFromVideoId = (stateId)=>{
getStateById(stateId).then((data)=>{
console.log(data)
if(data.error){
return console.log(data.error)
}
else{
setState(data.statename)
}
})
}
useEffect(() => {
getStateByFromVideoId(videoCategory.state);
}, []);
return (
<Container fluid>
<iframe
src={videoCategory.link}
width="300px"
height="300px"
id="myId"
className="myClassname"
display="initial"
position="relative"
allowfullscreen="allowfullscreen"
></iframe>
<Card style={{ width: "300px", }}>
<Card.Body>
<Card.Title>{videoCategory.name}</Card.Title>
<Card.Text>{videoCategory.description}</Card.Text>
<Card.Text>{state}</Card.Text>
<Card.Text>{videoCategory.location}</Card.Text>
<Button variant="primary">Go somewhere</Button>
</Card.Body>
</Card>
</Container>
);
};
export default VideoCard;
**UPDATE**
The API structure for State, which will reflect in card, the location is hardcoded, it doesn't have any id.
{
"_id": "5eb59d177693b6f99404f4c6",
"link": "https://www.youtube.com/embed/NEIwl93Yr8o",
"description": "LifeStyle",
"name": "Testing",
"state": "5eb564ec7693b6f99404f4c5",
"category": "5ead7555fb5c440f458e625b",
"location": "Kondapur",
"createdAt": "2020-05-08T17:55:35.731Z",
"updatedAt": "2020-05-08T18:28:43.635Z",
"__v": 0
},
** The Filter of State and location(citynames) is :**
{
"citynames": [
"ABC",
"EFG"
],
"_id": "5eb2ad8b554215be68773cf1",
"statename": "Madras",
"createdAt": "2020-05-06T12:28:59.149Z",
"updatedAt": "2020-05-06T12:28:59.149Z",
"__v": 0
},
Here's i change some code, but there's also some notes i need to confirm first
import React, { useState, useEffect } from "react";
import Menu from "../common/Menu";
import { Form, Container, Row, Col } from "react-bootstrap";
import {
getVideosBasedOnCategories,
getAllStates,
} from "./helper/userApiCalls";
import VideoCard from "../common/VideoCard";
const CategoryVideos = ({ match }) => {
const [videoCategory, setVideoCategory] = useState([]);
const [apiStates, setApiStates] = useState([])
const [selectedState, setSelectedState] = useState(null);
// Change default value to '' i think
const [selectedLocation, setSelectedLocation] = useState(null);
const preload = (categoryId) => {
getVideosBasedOnCategories(categoryId).then((data) => {
if (data.error || !data) {
console.log(data.error);
return <h1>No Data to Show Now</h1>;
} else {
setVideoCategory(...videoCategory, data);
}
});
};
//Intial Loading
useEffect(() => {
preload(match.params.categoryId);
getAllStateForSelect();
}, []);
//getting data for first Select Component from API
const getAllStateForSelect = () => {
getAllStates().then((data) => {
console.log(data);
if (data.error) {
return console.log(data.error);
} else {
setApiStates(...apiStates, data);
// remove this line
// setSelectedLocation(data[0])
}
});
};
const onStateChange = event => {
setSelectedState(event.target.value)
}
const onLocationChange= event => {
setSelectedLocation(event.target.value);
}
//Storing Location into Option for Second Select
const onSplit = (x) => {
var arr = [];
for (let i = 0; i < x.citynames.length; i++) {
arr.push(
<option key={i} value={x.citynames[i]}>
{x.citynames[i]}
</option>
);
}
return arr;
};
return (
<div>
<Menu />
<Container style={{ marginTop: "200px" }}>
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select State</Form.Label>
<Form.Control
as="select"
onChange={onStateChange}
custom
>
{apiStates.map((data, index) => (
<option name="setmap" key={index} value={data._id}>
{data.statename}
</option>
))}
</Form.Control>
</Form.Group>
</Form>
<Form>
<Form.Group controlId="exampleForm.SelectCustom">
<Form.Label>Select Location</Form.Label>
<Form.Control
as="select"
onChange={onLocationChange}
custom
>
{apiStates
.filter(apiState => apiState._id === selectedState)
.map(value => onSplit(value))}
</Form.Control>
</Form.Group>
</Form>
<Row>
// I'm curious about the location, is it array of string ?
{videoCategory.filter(videocard => videocard.location === selectedLocation))
.map((videocard) => {
return (
<Col lg={4} xs={12} md={12} className="py-3 px-3">
<VideoCard videoCategory={videocard} />
</Col>
);
})}
</Row>
</Container>
</div>
);
};
export default CategoryVideos;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>