I am trying to filter data based on set of checkboxes and display the updated data in my NextJs project. The data to be displayed is fetched from api(cspaces). "filteredList" is the filtered data which is being passed to the component.
Filter function:
let filteredList = cspaces;
const onFilterChange = (event, filter, rorb) => {
event.preventDefault();
if ( activeFilterPType.includes(filter)) {
const filterIndex = activeFilterPType.indexOf(filter);
const newFilter = [...activeFilterPType];
newFilter.splice(filterIndex, 1);
setActiveFilterPType(newFilter);
} else {
setActiveFilterPType([...activeFilterPType, filter]);
}
if (
activeFilterPType.length === 0 ||
activeFilterPType.length === filterList.length
) {
filteredList = cspaces;
// filteredList = cspaces.filter(item => item.attributes.leadType.includes(rorb))
}
else {
let reg1 = /Shop|Office|Showroom/
filteredList = cspaces.filter(item =>
activeFilterPType.includes((item.attributes.propertySubType.match(reg1))["0"])
)
filteredList = filteredList.filter(item => item.attributes.leadType.includes(rorb)
)
console.log('filteredlist -> ',filteredList)
}
}
The checkboxes:
...
{data.propertytype.items.map((tag,index) => (
<li key={tag.value}>
<Form.Check
type="checkbox"
id={tag.value}
name={tag.value}
label={tag.label}
value={tag.label}
onChange={(e)=> {handleCheckboxChange(e,tag.label)}}
/>
....
The handleCheckboxChange function:
...
const handleCheckboxChange = (e,value) => {
if(e.target.checked) setPType(value)
}
...
The component where I am passing filtered data is :
....
<ResultsTopBar data={filteredList} sortBy={data.sortby} sortData={sortTheKey}/>
{sortKey==='latest'?
filteredList && (
<Row>
{filteredList.map((filteredListItems, id) => (
<Col key={id} sm="6" lg="4" className="mb-5 hover-animate">
<CardCspace data={filteredListItems} />
</Col>
)
)}
</Row>
)
.....
Button on click the onFilterChange function is called:
<Button type="submit" onClick={(e) => onFilterChange(e, pType, rorb)}>
<FontAwesomeIcon icon={faFilter} className="me-1" />
Filter
</Button>
The data is getting filtered n displayed in console, but in web page shows all of the original response data without filtering. Please help!
Snapshot is below:
filteredList
I'm trying to render a table of data fetch from an API.
The first render is fine, everything seems to be ok, but when i'm trying to use SortBy or when i'm trying to add an input that i could use to call filtered data to the API i got the error :
react-dom.development.js:27292 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
at checkForNestedUpdates (react-dom.development.js:27292:1)
at scheduleUpdateOnFiber (react-dom.development.js:25475:1)
at dispatchReducerAction (react-dom.development.js:17452:1)
at react-table.development.js:944:1
at react-table.development.js:253:1
at commitHookEffectListMount (react-dom.development.js:23150:1)
at commitLayoutEffectOnFiber (react-dom.development.js:23268:1)
at commitLayoutMountEffects_complete (react-dom.development.js:24688:1)
at commitLayoutEffects_begin (react-dom.development.js:24674:1)
at commitLayoutEffects (react-dom.development.js:24612:1)
const TableAnime = () => {
const [animes, setAnimes] = useState({});
const [loadingData, setLoadingData] = useState(true);
const [endpoint, setEndpoint] = useState('/anime?page[limit]=10&page[offset]=0')
const getAnimes = async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}
useEffect(() => {
getAnimes()
}, []);
const handleForm = async () => {
const results = await axios.get(`https://kitsu.io/api/edge/anime`);
setAnimes(results.data)
console.log(animes)
}
const data = useMemo(() => (animes.data), [animes.data]);
return (
<div>
<Form handleForm={handleForm}/>
<h1>Catalogue</h1>
{loadingData ?
(
<p>Loading Please wait...</p>
) : (
<Table animes={animes.data} />
)
}
</div>
);
}
// == Export
export default TableAnime;
Table.js :
// == Import
import { useTable } from "react-table";
import { useMemo } from "react";
import { Link } from 'react-router-dom';
import moment from "moment"
import RowItem from "./RowItem";
import HeaderItem from "./HeaderItem";
// == Composant
const Table = ({animes}) => {
const columns = useMemo(() => [
{ Header: "Titre", accessor: "attributes.canonicalTitle"},
{ Header: "Titre Japonais", accessor: "attributes.titles.ja_jp"},
{ Header: "Age recommandé", accessor: "attributes.ageRatingGuide"},
{ Header: "Date de sortie", accessor: d => moment(d.attributes.startDate).format("DD/MM/YYYY")},
{ Header: "Rang", accessor: "attributes.popularityRank"},
{ Header: " ", accessor: d => <Link to={`/anime/${d.id}`}>Voir les détails</Link>},
]);
const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
} = useTable({columns, data: animes})
return (
<>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
<HeaderItem headerGroup={headerGroup} key={Date.now()}/>
))}
</thead>
<tbody {...getTableBodyProps()}>
{rows.map(row => {
prepareRow(row)
return (
<RowItem key={(Date.now()*Math.random())} row={row} />
)
})}
</tbody>
</table>
</>
);
}
// == Export
export default Table;
Then i got RowItem and HeaderItem :
const RowItem = ({row}) => {
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps() }>
{cell.render('Cell')}
</td>
)
})}
</tr>
);
}
export default RowItem
const HeaderItem = ({headerGroup}) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps()} >
{column.render('Header')}
</th>
))}
</tr>
);
export default HeaderItem ;
From what i understand that could be because i didn't use useMemo properly but i don't know how to do that. I tried at multiple places but nothing worked.
I'm using React Table for the first time and i'm new to React, so I'm sorry if the answer seems really easy but i really coudn't figured it out myself.
You need to cache the getAnime fetcher function by using useCallback and pass the endpoint as the dependency to make sure the fetcher fn is only called once (twice in strict mode).
So, change this:
const getAnimes = async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}
useEffect(() => {
getAnimes()
}, []);
into this:
const getAnimes = useCallback(async () => {
const response = await axios.get(`https://kitsu.io/api/edge${endpoint}`);
setAnimes(response.data);
setLoadingData(false);
}, [endpoint]);
useEffect(() => {
getAnimes();
}, [getAnimes]);
You can check here
I am creating a custom input via the component method with formkit.
I have the computed which looks like this
<template>
<label
class="InputCheckboxFeature"
:for="props.context.id"
>
<input
:id="props.context.id"
type="checkbox"
class="sr-only"
:checked="checked"
#input="handleInput"
:value="props.context._value"
/>
<span class="InputCheckboxFeature__indicator">
<span class="space-x-4 inline-flex items-center">
<span
v-if="props.context.attrs.icon"
class="InputCheckboxFeature__icon"
v-html="props.context.attrs.icon"
>
</span>
<span class="inline-block">
{{ props.context.label }}
</span>
</span>
<ButtonInfo
v-if="props.context.tooltip"
class="flex-shrink-0"
:text="props.context.tooltip"
aria-label="More Info"
/>
</span>
</label>
</template>
I then have a plugin that strips out all the HTML elements so the custom input will only output the component markup
export const createCleanInputPlugin = (): FormKitPlugin => {
return (node: FormKitNode) => {
node.on('created', () => {
const { props } = node;
if (
!(
props.type === InputType.FEATURED_CHECKBOX &&
props.definition &&
typeof props.definition.schema === 'function'
)
) {
return;
}
const definition = { ...props.definition };
const schema = definition.schema as FormKitExtendableSchemaRoot;
// We replace the schema function with our own higher-order-function
definition.schema = function (extensions = {}) {
const ext = {
...extensions,
...{
inner: { $el: null },
outer: { $el: null },
label: { $el: null },
wrapper: { $el: null },
},
};
// Finally we call the original schema, with our extensions applied
return schema(ext);
};
// Now we replace the input definition
props.definition = definition as FormKitTypeDefinition;
});
};
};
The problem I am facing is that when the element is rendered it is outputting the label twice.
Is it possible to hide the label, and only have it render in the component?
I'm having some trouble figuring out how to save the state in my app. I have a component where a user adds a 'NAME' and a 'WEIGHT'. When the user clicks the submit button, it redirects them to the Home Page and the newly added name is displayed (the weight will be displayed elsewhere).
What I'm having trouble with is when I go back and add another 'NAME' and 'WEIGHT', the previous name disappears and is replaced with the new one. What I would like to happen is have the previous 'NAME' stay on the Home Page when I add a new one.
Here is my AddPage component:
const AddPage = () => {
const [name, setName] = useState('');
const [weight, setWeight] = useState(0);
const classes = useStyles();
const dispatch = useDispatch();
return (
<div>
<Header title="Add Page" />
<div className={classes.addPage}>
<div className={classes.addMovementDiv}>
<TextField
className={classes.movementName}
key="name"
label="Enter Movement Name"
InputProps= {{className: "textBoxColor"}}
variant="outlined"
onChange={event => {
const { value } = event.target;
setName(value);
}}
/>
<TextField
className={classes.movementWeight}
key="weight"
label="Enter Movement Weight"
type="number"
variant="outlined"
onChange={event => {
const { value } = event.target;
setWeight(value);
}}
InputProps= {{endAdornment: <InputAdornment position="end">lb</InputAdornment>, className: "textBoxColor"}} />
<Button
className={classes.addButton}
variant="outlined"
onClick={() => dispatch(addMovement(name, weight))}
>
<AddCircleIcon />
</Button>
</div>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
weight: state.move.weight,
}
};
const mapDispatchToProps = (dispatch) => {
return({
addMovement: (name, weight) => dispatch(addMovement(name, weight)),
})
};
const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
export default compose(withConnect)(AddPage);
Here is my HomePage component:
const HomePage = () => {
const classes = useStyles();
const name = useSelector(state => state.move.name);
const displayMovementButtons = () => {
if (name) {
return (
<Button
className={classes.movementButtons}
onClick={() => history.push('/movement/:id')}
>
<div className={classes.movementName} >{name}</div>
</Button>
)
}
return <div className={classes.noMovementsMessage} >Click add button to begin</div>
}
return (
<div className={classes.homePageContent} >
<Header title={"Home Page" }/>
<div>{displayMovementButtons()}</div>
<div className={classes.fabDiv}>
<Fab
className={classes.fab}
onClick={() => history.push(`/add`)}>
<AddIcon />
</Fab>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
}
};
const withConnect = connect(
mapStateToProps,
);
export default compose(withConnect)(HomePage);
Here is my reducer:
const initialState = []
const addMovementReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_MOVEMENT:
return [ ...state, {name: action.name, weight: action.weight} ]
default:
return state;
}
};
export default addMovementReducer;
Here is where my store is set up:
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(reduxThunk))
);
ReactDOM.render(
<Provider store={store} >
<App />
</Provider>,
document.querySelector('#root')
);
Any help would be appreciated!
you're misusing redux state by using it along side with hooks. it's an either or situation
may I see your store and reducer ?
In my react application I'm using Material-UI enhanced table which is based on react-table and I would like to change the style of their rows.
Reading from documentation (https://material-ui.com/api/table-row/) in the component TableRow should be used the prop "classes" to change the style, but in the MaterialUi code props are read this way:
<TableRow {...row.getRowProps()}>
My question is how can I use the prop classes if the TableRow props are added automatically? I thought I needed to have this:
<TableRow classes="rowStyle ">
where rowStyle is:
const styles = {
rowStyle : {
padding: 10,
border: "1px solid red"
}
};
But obviously I can't this way, how can I add "classes" to the getRowProps() and the new style in it?
I couldn't find an explanation or a good example in official documentation or stackOverflow
Many thanks for the help
EnhancedTable.js:
import React from "react";
import Checkbox from "#material-ui/core/Checkbox";
import MaUTable from "#material-ui/core/Table";
import PropTypes from "prop-types";
import TableBody from "#material-ui/core/TableBody";
import TableCell from "#material-ui/core/TableCell";
import TableContainer from "#material-ui/core/TableContainer";
import TableFooter from "#material-ui/core/TableFooter";
import TableHead from "#material-ui/core/TableHead";
import TablePagination from "#material-ui/core/TablePagination";
import TablePaginationActions from "./TablePaginationActions";
import TableRow from "#material-ui/core/TableRow";
import TableSortLabel from "#material-ui/core/TableSortLabel";
import TableToolbar from "./TableToolbar";
import {
useGlobalFilter,
usePagination,
useRowSelect,
useSortBy,
useTable,
} from "react-table";
const IndeterminateCheckbox = React.forwardRef(
({ indeterminate, ...rest }, ref) => {
const defaultRef = React.useRef();
const resolvedRef = ref || defaultRef;
React.useEffect(() => {
resolvedRef.current.indeterminate = indeterminate;
}, [resolvedRef, indeterminate]);
return (
<div>
<Checkbox ref={resolvedRef} {...rest} />
</div>
);
}
);
const inputStyle = {
padding: 0,
margin: 0,
border: 0,
background: "transparent",
};
// Create an editable cell renderer
const EditableCell = ({
value: initialValue,
row: { index },
column: { id },
updateMyData, // This is a custom function that we supplied to our table instance
}) => {
// We need to keep and update the state of the cell normally
const [value, setValue] = React.useState(initialValue);
const onChange = (e) => {
setValue(e.target.value);
};
// We'll only update the external data when the input is blurred
const onBlur = () => {
updateMyData(index, id, value);
};
// If the initialValue is changed externall, sync it up with our state
React.useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return (
<input
style={inputStyle}
value={value}
onChange={onChange}
onBlur={onBlur}
/>
);
};
EditableCell.propTypes = {
cell: PropTypes.shape({
value: PropTypes.any.isRequired,
}),
row: PropTypes.shape({
index: PropTypes.number.isRequired,
}),
column: PropTypes.shape({
id: PropTypes.number.isRequired,
}),
updateMyData: PropTypes.func.isRequired,
};
// Set our editable cell renderer as the default Cell renderer
const defaultColumn = {
Cell: EditableCell,
};
const EnhancedTable = ({
columns,
data,
setData,
updateMyData,
skipPageReset,
}) => {
const {
getTableProps,
headerGroups,
prepareRow,
page,
gotoPage,
setPageSize,
preGlobalFilteredRows,
setGlobalFilter,
state: { pageIndex, pageSize, selectedRowIds, globalFilter },
} = useTable(
{
columns,
data,
defaultColumn,
autoResetPage: !skipPageReset,
// updateMyData isn't part of the API, but
// anything we put into these options will
// automatically be available on the instance.
// That way we can call this function from our
// cell renderer!
updateMyData,
},
useGlobalFilter,
useSortBy,
usePagination,
useRowSelect,
(hooks) => {
hooks.allColumns.push((columns) => [
// Let's make a column for selection
{
id: "selection",
// The header can use the table's getToggleAllRowsSelectedProps method
// to render a checkbox. Pagination is a problem since this will select all
// rows even though not all rows are on the current page. The solution should
// be server side pagination. For one, the clients should not download all
// rows in most cases. The client should only download data for the current page.
// In that case, getToggleAllRowsSelectedProps works fine.
Header: ({ getToggleAllRowsSelectedProps }) => (
<div>
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
</div>
),
// The cell can use the individual row's getToggleRowSelectedProps method
// to the render a checkbox
Cell: ({ row }) => (
<div>
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
</div>
),
},
...columns,
]);
}
);
const handleChangePage = (event, newPage) => {
gotoPage(newPage);
};
const handleChangeRowsPerPage = (event) => {
setPageSize(Number(event.target.value));
};
const removeByIndexs = (array, indexs) =>
array.filter((_, i) => !indexs.includes(i));
const deleteUserHandler = (event) => {
const newData = removeByIndexs(
data,
Object.keys(selectedRowIds).map((x) => parseInt(x, 10))
);
setData(newData);
};
const addUserHandler = (user) => {
const newData = data.concat([user]);
setData(newData);
};
// Render the UI for your table
return (
<TableContainer>
<TableToolbar
numSelected={Object.keys(selectedRowIds).length}
deleteUserHandler={deleteUserHandler}
addUserHandler={addUserHandler}
preGlobalFilteredRows={preGlobalFilteredRows}
setGlobalFilter={setGlobalFilter}
globalFilter={globalFilter}
/>
<MaUTable {...getTableProps()}>
<TableHead>
{headerGroups.map((headerGroup) => (
<TableRow {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<TableCell
{...(column.id === "selection"
? column.getHeaderProps()
: column.getHeaderProps(column.getSortByToggleProps()))}
>
{column.render("Header")}
{column.id !== "selection" ? (
<TableSortLabel
active={column.isSorted}
// react-table has a unsorted state which is not treated here
direction={column.isSortedDesc ? "desc" : "asc"}
/>
) : null}
</TableCell>
))}
</TableRow>
))}
</TableHead>
<TableBody>
{page.map((row, i) => {
prepareRow(row);
return (
<TableRow {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<TableCell {...cell.getCellProps()}>
{cell.render("Cell")}
</TableCell>
);
})}
</TableRow>
);
})}
</TableBody>
<TableFooter>
<TableRow>
<TablePagination
rowsPerPageOptions={[
5,
10,
25,
{ label: "All", value: data.length },
]}
colSpan={3}
count={data.length}
rowsPerPage={pageSize}
page={pageIndex}
SelectProps={{
inputProps: { "aria-label": "rows per page" },
native: true,
}}
onChangePage={handleChangePage}
onChangeRowsPerPage={handleChangeRowsPerPage}
ActionsComponent={TablePaginationActions}
/>
</TableRow>
</TableFooter>
</MaUTable>
</TableContainer>
);
};
EnhancedTable.propTypes = {
columns: PropTypes.array.isRequired,
data: PropTypes.array.isRequired,
updateMyData: PropTypes.func.isRequired,
setData: PropTypes.func.isRequired,
skipPageReset: PropTypes.bool.isRequired,
};
export default EnhancedTable;
Correct me if i'm wrong, but i think the first step to your solution is the correct definition of styles for the material-ui element. I can't say for certain if there is another way to generate these styles, but a simple object such as:
const inputStyle = { padding: 0, margin: 0, border: 0, background: "transparent", };
will probably not work. You probably have to use the material styles for that.
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
border: "1px solid red",
padding: 10
},
});
and then you have to use the style hook in the component definition:
const classes = useStyles();
When overriding a material-ui definition this part is the important one to define which part is going to overriden (not allowed to include pictures yet, sorry):
Material-Ui CSS keys for Table Row
Then you can override the style <TableRow classes={{ root: classes.root }}> or in your case maybe more like <TableRow classes={{ root: classes.root }} {...row.getRowProps()}>
An additional problem you might face is that you have to override the style of the TableCells too, because the overlap the TableRow border.
I have here a code sandbox that is by no means perfect, but should help you on the right track: https://codesandbox.io/s/material-demo-535zq?file=/demo.js
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(() => ({
rowStyle : {
padding: 10,
border: "1px solid red"
}
}));
const EnhancedTable = ()=>{
const classes = useStyles();
return(
<TableRow className={classes.rowStyle}/>
)
}