Problem trying conditional rendering in map() method in ReactJS - javascript

I'm having a problem where I'm using map method to create a table, then on each row of the table I have a Button that triggers conditional rendering based on state (true/false).
The problem is that all conditional renders are based on the same state this.state.displayColorPicker ? So when you click the button on one row it triggers the conditional render on all the rows.
Here is my function that renders the body of the Table :
display_table = () => {
if (!this.state.data) {
return <tr />
}
if (!this.state.data[1]) {
return <tr />
}
let xx = this.state.valuesplayers;
let i=-1;
let j=-1;
let k=500;
let l = -400;
if (typeof xx !== 'undefined' && xx !== 's') {
let xx3 = xx.map((player_row, row) =>{
i = i+1;
return (<tr className='dataRow' key={i} id={i}>{
player_row.map((player_col, col)=>{
if (this.state.data[1][col].length > 2) {
k=k+1;
return(<td key={'displayTable'+k} style={{backgroundColor: this.state.valuesplayers[row][col] === this.state.savedData[row][col] ? '' : 'yellow'}}><select key={this.state.valuesplayers[row][col]+k+0.5} value={this.state.valuesplayers[row][col]} onChange={e => this.handleChangeCell(e, row, col)}>
{this.state.data[1][col].map((DefValue, index) => {
i+=1;
if (index >= 2) {
return(<option key={'option' +i}>{DefValue}</option>);
}
return'';
})}
</select></td>)
} else if (this.state.data[1][col][0] === "Color") {
l = l+1;
const popover = {
position: 'absolute',
zIndex: '2',
top :'55px',
right:'0px',
}
const cover = {
position:'fixed',
top: '0px',
right: '0px',
bottom: '0px',
left: '0px',
}
return(<td className='SliderPicker' key={'couleur'+l} style={{backgroundColor: this.state.valuesplayers[row][col] === this.state.savedData[row][col] ? '' : 'yellow'}}>
<div className='ColorWrapper'>
<button className='colorButton' style={{borderColor: this.state.valuesplayers[row][col] === this.state.savedData[row][col] ? '' : this.state.valuesplayers[row][col],
borderWidth: this.state.valuesplayers[row][col] === this.state.savedData[row][col] ? '' : '5px' }} onClick={e => this.handleClick(e) }>Pick Color
</button>
{this.state.displayColorPicker ?
<div style={ popover }>
<div style={ cover } onClick={e => this.handleClose(e) }/>
<SketchPicker key={'ColorSelector'+l+1.5} type='color' color={this.state.valuesplayers[row][col]} value={this.state.valuesplayers[row][col]}
onChange={color => this.handleChangeColor(color, row, col)}/>
</div> : null }
</div>
</td>)
}
else {
j+=2;
return(<td key={'cellule'+j} style={{backgroundColor: this.state.valuesplayers[row][col] === this.state.savedData[row][col] ? '' : 'yellow'}}>
<input key={'inputText'+k+1.5} type="text" className='inputTable' value={this.state.valuesplayers[row][col]}
onChange={e => this.handleChangeCell(e, row, col)}/></td>)
}
})
}
<td className='ButtonRow' key={'buttonRow'+i}>
<button key={'DelRow' + i} onClick={this.delARow} className='DeleteRow'>X</button>
<button key={'AddRow' + i} onClick={this.addARow} className='AddRow'>+</button>
</td></tr>
)
})
return(xx3);
}
else {
return <td key='display table error'/>
}
}
Currently my state is declared in the constructor method :
constructor(props) {
super(props);
this.state = {
displayColorPicker: false,
};
}
If someone could help me to map a different state for each row (or any other idea that could work)
Thanks in advance.
Edit : some HandleChange functions :
handleChangeCell(e, row, col) {
let newplayers2 = this.state.valuesplayers;
newplayers2[row][col] = e.target.value;
this.setState({valuesplayers: newplayers2});
}
handleChangeColor(color, row, col) {
this.setState({color: color.hex});
console.log('color', color.hex)
let newplayers2 = this.state.valuesplayers;
newplayers2[row][col] = color.hex;
this.setState({valuesplayers: newplayers2});
}

Related

Warning: Text content did not match in React 18

recently the project I am working on has been upgraded to React 18. By then, suddenly a lot of issues with hydration have started to appear as warnings/errors in the console. The one I'm struggling with is "Warning: Text content did not match":
Code of this component:
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
I know that this issue results from the difference between client and server side rendering, but I don't know how to fix it and no other similar question contained solution that worked in my case.
Rest of the file, in case if the issue is not with aforementioned part:
import React, { useMemo, useState } from 'react';
import type { ReactElement } from 'react';
import AnimateHeight from 'react-animate-height';
import { BaseSelect, SelectOption } from '../molecules/base-select';
import type { FrequentlyAskedCategory } from './frequentlyAskedQuestion';
import { fromBase64 } from '../shared-services/base64Service';
interface FaqPanelProps {
mainTitle?: string;
menuTitle?: string;
showOnlyAnswer: boolean;
currentPageUrl: string;
questionsByCategories: Record<string, FrequentlyAskedCategory>,
faqStructuredDataBase64: string;
}
const FAQPanel = (props: FaqPanelProps): ReactElement => {
const categories = Object.keys(props.questionsByCategories);
const dataForSelect: Array<SelectOption> = categories.map(key => ({ label: props.questionsByCategories[key].name, value: key }));
const noOpenedElementIndex = -1;
const [openedElement, setOpenedElement] = useState<number>(-1);
const [selectedCategory, setSelectedCategory] = useState<SelectOption>(dataForSelect.length > 0 ? dataForSelect[0] : null);
const [transitionDisabled, setTransitionDisabled] = useState<boolean>(false);
const parsedStructuredData = useMemo(() => {
if(props.faqStructuredDataBase64 != null && props.faqStructuredDataBase64 !== ""){
return fromBase64(props.faqStructuredDataBase64);
}
return "";
}, [props.faqStructuredDataBase64]);
const selectNewCategory = (option: SelectOption): void => {
setTransitionDisabled(true);
setOpenedElement(noOpenedElementIndex);
setSelectedCategory(option);
}
const openElement = (index: number): void => {
if (transitionDisabled) {
setTransitionDisabled(false);
}
setOpenedElement(index === openedElement ? noOpenedElementIndex : index);
}
const speakableJson = JSON.stringify({
"#context": "https://schema.org/",
"#type": "WebPage",
"name": props.mainTitle,
"speakable":
[".O75-product-faq__headline",
".O75-product-faq__questions__item"],
"url": props.currentPageUrl
});
const hasFAQStructuredData = parsedStructuredData != null && parsedStructuredData !== "";
return (
<div className="container">
<section className="O75-product-faq" >
{
props.mainTitle
? <h2 className="O75-product-faq__headline">{props.mainTitle}</h2>
: <h4 className="O75-product-faq__categories-headline">{props.menuTitle}</h4>
}
<div className="flex">
{dataForSelect.length > 1 &&
<div className="O75-product-faq__categories">
<div className="filter__list is-hidden-sm filter">
{
dataForSelect.map((element, i) => (
<button className={`filter__btn js-filter__btn${element.value === selectedCategory?.value ? " is-active" : ""}`} key={i} onClick={() => selectNewCategory(element)}>
{element.label}
</button>))
}
</div>
<div className="filter__group is-hidden-md">
<BaseSelect selectedValue={selectedCategory}
handleChange={selectNewCategory}
options={dataForSelect} />
</div>
</div>
}
{categories.length > 0 &&
<div className="O75-product-faq__questions is-active accordion--initialized">
{
dataForSelect.length > 1 && <h4 className="O75-product-faq__questions__name js-category-name">{props.questionsByCategories[selectedCategory?.value].name}</h4>
}
{
props.questionsByCategories[selectedCategory?.value].questions.map((element, i) => (
<div key={i} className="O75-product-faq__questions__item">
{(element.question || props.showOnlyAnswer) && <div className={`O75-product-faq__questions__item__button${openedElement === i ? ' has-accordion-open' : ''}`} onClick={() => openElement(i)}>{element.question}</div>}
<AnimateHeight height={openedElement === i ? 'auto' : 0} duration={transitionDisabled ? 0 : 400}>
<div className="O75-product-faq__questions__item__content" dangerouslySetInnerHTML={{ __html: element.answer }} />
</AnimateHeight>
</div>))
}
</div>
}
</div>
{hasFAQStructuredData && <script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html:parsedStructuredData } }></script>}
<script suppressHydrationWarning type="application/ld+json" dangerouslySetInnerHTML={{__html: speakableJson}} ></script>
</section>
</div>
)
}
export { FAQPanel };
export type { FaqPanelProps }
Does anyone knows how to fix it?

DevExpress Reactive Get all filters when you use custom filters

I want get all filters for the GridExport but when i use onFiltersChange my filter is deselect, but when I delete onFiltersChange it's work properly.
I use hook useState for setFilters ans filters.
I use this version in my package.json:
"#devexpress/dx-react-chart": "^2.6.4",
"react": "^16.13.1",
How can I do this ?
This is my grid:
<Grid
rows={items}
columns={columns}>
<StockQuantityTypeProvider for={["stockQuantity"]}/>
<DateTypeProvider for={["lastCountingPeriodUpdate"]}/>
<SearchState/>
<SortingState sorting={sorting} onSortingChange={setSorting}/>
<FilteringState filters={filters} onFiltersChange={setFilters}/>
<PagingState defaultPageSize={20}/>
<SelectionState selection={itemsSelectedIndex} onSelectionChange={onChangeSelection}/>
<IntegratedSorting/>
<IntegratedFiltering/>
<IntegratedPaging/>
<IntegratedSelection/>
<Table
messages={{noData: "Aucun article"}}
rowComponent={tableRow}
/>
{currentUser?.authorization?.hasWriteRights && <TableSelection showSelectAll/>}
<Toolbar/>
<SearchPanel messages={{searchPlaceholder: "Rechercher..."}}/>
<CustomToolbarMarkup onClickRefresh={onClickRefresh}>
<CustomToolbarButton/>
</CustomToolbarMarkup>
<TableColumnResizing/>
<TableHeaderRow showSortingControls/>
<TableFilterRow
showFilterSelector
cellComponent={(props) =>
<CustomFilterCell {...props} rows={items} columns={[
{columnName: "baseUnitOfMeasure", type: "selection"},
{columnName: "lastCountingPeriodUpdate", type: "dateRange"},
]}/>
}
/>
<PagingPanel pageSizes={[20, 40, 60]}/>
<ExportPanel startExport={startExport}/>
</Grid>
<GridExporter
ref={exporterRef}
columns={columns}
rows={items}
sorting={sorting}
filters={filters}
selection={itemsSelectedIndex}
onSave={onSave}
/>
And this is my custom filter:
const SelectionFilterCell = ({filter, onFilter, rows, property}) => {
const saveUnitOfMeasure = Array.from(new Set(rows.map((row) => row[property])));
return (
<th style={{fontWeight: "normal"}}>
<Input
type={"select"}
className="form-control stock-propertyTab-inputSelect"
value={filter ? filter.value : ""}
onChange={(e) => onFilter(e.target.value ? {value: e.target.value, operation: "equal"} : null)}>
<option key={-1} value="">
Filtrer...
</option>
{
// eslint-disable-next-line
saveUnitOfMeasure.map((unit, index) => {
if (unit !== null && unit !== "") {
return (
<option key={index} value={unit}>
{unit}
</option>
);
}
})
}
</Input>
</th>
);
};
export const CustomFilterCell = (props) => {
const {column,rows,columns}=props;
let isCustom = false;
let propertyUse = undefined;
columns.forEach((c) => {
if (!isCustom) {
if (column.name === c.columnName) {
isCustom = true;
propertyUse = c;
}
}
});
if (isCustom && propertyUse !== undefined) {
if (propertyUse.type === "selection") {
return <SelectionFilterCell {...props} content={rows} property={propertyUse.columnName} />;
}
}
return <TableFilterRow.Cell {...props} />;
};

How to keep receiving data while keeping data in state in order?

I'm working on this react table sorting when the user clicks on table header it needs to sort the table, sorting is working but the problem is I'm receiving new data every second through SignalR hub and it sets state udata to new data. When a user clicks on table header it sorts the table but again goes back to the new state changed by new data. And cancells the sorted table back to unsorted.
Is there any way I can keep sorted state and still receive data?
I'm new to react any help would be appreciated
constructor() {
super()
this.state = {
udata: [],
sort: {
column: null,
direction: 'desc',
},
}
}
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl('/signalserver')
.build()
connection
.start()
.then(function() {})
.catch(function(err) {
return console.error(err.toString())
})
connection.on(
'APIChannel',
function(data) {
this.setState({udata: data})
}.bind(this),
)
async function start() {
try {
await connection.start()
console.log('connected')
} catch (err) {
console.log(err)
setTimeout(() => start(), 5000)
}
}
connection.onclose(async () => {
await start()
})
}
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.state.udata.sort((a, b) => {
if (column === 'appName') {
// This sorts strings taking into consideration numbers in strings.
// e.g., Account 1, Account 2, Account 10. Normal sorting would sort it Account 1, Account 10, Account 2.
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
sortedData.reverse()
}
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
renderItem(item, key) {
const itemRows = [
<tr onClick={clickCallback} key={'row-data-' + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
'fa fa-circle text-c-' +
(item.appState === 'STARTED' ? 'green' : 'red') +
' f-10 m-r-15'
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>,
]
return itemRows
}
render() {
let allItemRows = []
this.state.udata.forEach((item, key) => {
const perItemRows = this.renderItem(item, key)
allItemRows = allItemRows.concat(perItemRows)
})
return (
<Aux>
<Row>
<Table hover responsive>
<thead>
<tr>
<th className="sortable" onClick={this.onSort('appName')}>
{' '}
Account Name
</th>
<th> State</th>
<th> Space</th>
<th> Organization</th>
<th className="sortable" onClick={this.onSort('appUpdatedAt')}>
{' '}
Updated At
</th>
</tr>
</thead>
<tbody> {allItemRows}</tbody>
</Table>
</Row>
</Aux>
)
}
Move the sorting part of the function to new a function:
const sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
}
You can use this function in componentDidMount before setting the state with the newData and also in onSort function.
onSort(column) {
return function(e) {
let direction = this.state.sort.direction
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === 'asc' ? 'desc' : 'asc'
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction)
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction,
},
})
}.bind(this) // Bind "this" again because the onSort function is returning another function.
}
componentDidMount:
componentDidMount() {
// Code
connection.on(
'APIChannel',
function(data) {
let sortedData = []
if (this.state.sort.column) {
sortedData = this.sortData(data, this.state.sort.column,
this.state.sort.direction)
} else {
sortedData = data
}
this.setState({udata: sortedData})
}.bind(this),
)
// Rest of the code
}
EDIT:
import React, { Component } from "react";
import { Row, Col, Form, Card, Table, Tab, Nav } from "react-bootstrap";
import Aux from "../../hoc/_Aux";
import * as signalR from "#aspnet/signalr";
class Dashboard extends Component {
constructor() {
super();
this.state = {
udata: [],
sysdata: [],
expandedRows: [],
user: "active",
system: "",
data: [],
UserFilters: {
appState: [],
orgName: [],
spaceName: []
},
SysFilters: {
appState: []
},
intervalId: 0, //Scroll on top feature
sort: {
column: null,
direction: "desc"
}
};
}
sortData = (data, column, direction) => {
// Sort ascending.
const sortedData = data.sort((a, b) => {
if (column === 'appName') {
const collator = new Intl.Collator(undefined, {
numeric: true,
sensitivity: 'base',
})
return collator.compare(a.appName, b.appName)
} else {
return a.contractValue - b.contractValue
}
})
// Reverse the order if direction is descending.
if (direction === 'desc') {
return sortedData.reverse()
}
return sortedData
};
componentDidMount() {
let connection = new signalR.HubConnectionBuilder()
.withUrl("/signalserver")
.build();
connection
.start()
.then(function () { })
.catch(function (err) {
return console.error(err.toString());
});
connection.on(
"SBUserBrodcasting",
function (data) {
let sortedData = [];
if (this.state.sort.column) {
sortedData = this.sortData(
data,
this.state.sort.column,
this.state.sort.direction
);
} else {
sortedData = data;
}
this.setState({ udata: sortedData });
}.bind(this)
);
connection.on(
"SBSystemBrodcasting",
function (data) {
this.setState({ sysdata: data });
}.bind(this)
);
async function start() {
try {
await connection.start();
console.log("connected");
} catch (err) {
console.log(err);
setTimeout(() => start(), 5000);
}
}
connection.onclose(async () => {
await start();
});
}
onSort(column) {
return function (e) {
let direction = this.state.sort.direction;
if (this.state.sort.column === column) {
// Change the sort direction if the same column is sorted.
direction = this.state.sort.direction === "asc" ? "desc" : "asc";
}
// Sort ascending.
const sortedData = this.sortData(this.state.udata, column, direction);
// Set the new state.
this.setState({
udata: sortedData,
sort: {
column,
direction
}
});
}.bind(this); // Bind "this" again because the onSort function is returning another function.
}
scrollStep() {
if (window.pageYOffset === 0) {
clearInterval(this.state.intervalId);
}
window.scroll(0, window.pageYOffset - this.props.scrollStepInPx);
}
scrollToTop() {
let intervalId = setInterval(
this.scrollStep.bind(this),
this.props.delayInMs
);
this.setState({ intervalId: intervalId });
}
FilterUserArray = (array, UserFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(UserFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!UserFilters[key].length) return true;
return UserFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
FilterSysArray = (array, SysFilters) => {
let getValue = value =>
typeof value === "string" ? value.toUpperCase() : value;
const filterKeys = Object.keys(SysFilters);
return array.filter(item => {
// validates all filter criteria
return filterKeys.every(key => {
// ignores an empty filter
if (!SysFilters[key].length) return true;
return SysFilters[key].find(
filter => getValue(filter) === getValue(item[key])
);
});
});
};
HandleRowClick(rowId) {
const currentExpandedRows = this.state.expandedRows;
const isRowCurrentlyExpanded = currentExpandedRows.includes(rowId);
const newExpandedRows = isRowCurrentlyExpanded
? currentExpandedRows.filter(id => id !== rowId)
: currentExpandedRows.concat(rowId);
this.setState({ expandedRows: newExpandedRows });
}
SpaceRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBSpace-" + item}
label={item}
onChange={this.UserAppSpaceFilter.bind(this)}
/>
</li>
];
return itemRows;
}
OrgRenderFilterList(item, key) {
const itemRows = [
<li key={"li-data-" + key}>
<Form.Check
custom
type="checkbox"
value={item}
id={"SBOrg-" + item}
label={item}
onChange={this.UserAppOrgFilter.bind(this)}
/>
</li>
];
return itemRows;
}
RenderItem(item, key) {
const clickCallback = () => this.HandleRowClick(key);
const itemRows = [
<tr onClick={clickCallback} key={"row-data-" + key}>
<td>{item.appName}</td>
<td>
<h6 className="text-muted">
<i
className={
"fa fa-circle text-c-" +
(item.appState === "STARTED" ? "green" : "red") +
" f-10 m-r-15"
}
/>
{item.appState}
</h6>
</td>
<td>{item.spaceName}</td>
<td>
<h6 className="text-muted">{item.orgName}</h6>
</td>
<td>
<h6 className="text-muted">
{new Date(item.appUpdatedAt).toLocaleString()}
</h6>
</td>
</tr>
];
if (this.state.expandedRows.includes(key)) {
itemRows.push(
<tr key={"row-expanded-" + key}>
<td colSpan="6">
<Card className="card-event">
<Card.Body>
<div className="row align-items-center justify-content-center">
<div className="col">
<h5 className="m-0">Upcoming Event</h5>
</div>
<div className="col-auto">
<label className="label theme-bg2 text-white f-14 f-w-400 float-right">
34%
</label>
</div>
</div>
<h2 className="mt-2 f-w-300">
45<sub className="text-muted f-14">Competitors</sub>
</h2>
<h6 className="text-muted mt-3 mb-0">
You can participate in event{" "}
</h6>
<i className="fa fa-angellist text-c-purple f-50" />
</Card.Body>
</Card>
</td>
</tr>
);
}
return itemRows;
}
onClickfn = () => {
this.setState({ user: "active", system: "inactive" });
};
onClickfnsys = () => {
this.setState({ user: "inactive", system: "active" });
};
UserAppStateFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, appState: options }
});
}
UserAppSpaceFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.spaceName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, spaceName: options }
});
}
UserAppOrgFilter(e) {
let index;
// current array of options
const options = this.state.UserFilters.orgName;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
UserFilters: { ...this.state.UserFilters, orgName: options }
});
}
SysAppStateFilter(e) {
let index;
// current array of options
const options = this.state.SysFilters.appState;
// check if the check box is checked or unchecked
if (e.target.checked) {
// add the numerical value of the checkbox to options array
options.push(e.target.value);
} else {
// or remove the value from the unchecked checkbox from the array
index = options.indexOf(e.target.value);
options.splice(index, 1);
}
// update the state with the new array of options
this.setState({
SysFilters: { ...this.state.SysFilters, appState: options }
});
}
render() {
let Spacefilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.spaceName))).forEach(
(item, key) => {
const perItemRows = this.SpaceRenderFilterList(item, key);
Spacefilterlist = Spacefilterlist.concat(perItemRows);
}
);
let Orgfilterlist = [];
Array.from(new Set(this.state.udata.map(item => item.orgName))).forEach(
(item, key) => {
const perItemRows = this.OrgRenderFilterList(item, key);
Orgfilterlist = Orgfilterlist.concat(perItemRows);
}
);
let allItemRows = [];
this.FilterUserArray(this.state.udata, this.state.UserFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
allItemRows = allItemRows.concat(perItemRows);
}
);
let sysallItemRows = [];
this.FilterSysArray(this.state.sysdata, this.state.SysFilters).forEach(
(item, key) => {
const perItemRows = this.RenderItem(item, key);
sysallItemRows = sysallItemRows.concat(perItemRows);
}
);
return (
<Aux>
<Row>
<Col sm={12}>
<Tab.Container defaultActiveKey="user">
<Row>
<Col sm={2}>
<Nav variant="pills" className="flex-column">
<Nav.Item>
<Nav.Link eventKey="user" onClick={this.onClickfn}>
User
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="system" onClick={this.onClickfnsys}>
System
</Nav.Link>
</Nav.Item>
</Nav>
<br />
<Card
style={{
display: this.state.user === "active" ? "" : "none"
}}
>
<Tab.Pane eventKey="user">
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group onReset={this.handleFormReset}>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox1"
value="STARTED"
label="STARTED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="checkbox2"
value="STOPPED"
label="STOPPED"
onChange={this.UserAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
<h6>By Space</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Spacefilterlist}</Form.Group>
</ul>
<h6>By Organization</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>{Orgfilterlist}</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
<Card>
<Tab.Pane
eventKey="system"
style={{
display: this.state.system === "active" ? "" : "none"
}}
>
<Card.Header>
<Card.Title as="h5">Filters</Card.Title>
</Card.Header>
<Card.Body>
<h6>By State</h6>
<hr />
<ul className="list-inline m-b-0">
<Form.Group>
<li>
<Form.Check
custom
type="checkbox"
id="chec1"
value="STARTED"
label="STARTED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
<li>
<Form.Check
custom
type="checkbox"
id="chec2"
value="STOPPED"
label="STOPPED"
onChange={this.SysAppStateFilter.bind(this)}
/>
</li>
</Form.Group>
</ul>
</Card.Body>
</Tab.Pane>
</Card>
</Col>
<Col sm={10}>
<Tab.Content>
<Tab.Pane eventKey="user">
<Table hover responsive>
<thead>
<tr>
<th
className="sortable"
onClick={this.onSort("appName")}
>
Account Name
</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th
className="sortable"
onClick={this.onSort("appUpdatedAt")}
>
Updated At
</th>
</tr>
</thead>
<tbody>{allItemRows}</tbody>
</Table>
</Tab.Pane>
<Tab.Pane eventKey="system">
<Table hover responsive>
<thead>
<tr>
<th>App Name</th>
<th>State</th>
<th>Space</th>
<th>Organization</th>
<th>Updated At</th>
</tr>
</thead>
<tbody>{sysallItemRows}</tbody>
</Table>
</Tab.Pane>
</Tab.Content>
</Col>
</Row>
</Tab.Container>
</Col>
<button
id="myBtn"
title="Back to top"
className="scroll"
onClick={() => {
this.scrollToTop();
}}
>
<span className="feather icon-chevron-up" />
</button>
</Row>
</Aux>
);
}
}
export default Dashboard;
Use a parent component to perform the request and pass the unsorted values and sorting order value to a child component.
child component(table component most likely) will display data based on sorting order value.
Currently your component get mounted each time you change the state values
Add a life cycle method called componentWillReceiveProps(nextProps) or you can as well use static getDerivedStateFromProps(props, state) to perform the sorting inside of this method, that way when new data is available it will automatically be sorted alongside with the original one that was there. hence all your other codes remain the same and the new data just takes it rightful place in the sorting.

How to detect time without clicking on an element in react

I have a navigation menu with 3 levels. I would like to know how it can be detected if more than 1 minute has passed without the user clicking on any of the elements (parents and children).
When I spent 1 minute I would change showdesplegar: false. Does anyone know how it could be done? I have no idea
This is my original Nav code:
class Nav extends Component {
constructor(props){
super(props);
["desplegarClick",].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
addClassMenu: false,
addClassNav: false,
showtabs: this.props.showtabs
}
}
showHideSubmenu(index){
this.setState({
showfstmenu: index,
showdesplegar: true,
});
}
desplegarClick(){
this.setState({
showfstmenu: false,
showdesplegar: false,
});
}
render(){
const renderMenu = items => {
return (
<ul className="list">
{items.map((i, index) => {
var icoJson;
if(i.ico){
icoJson = <Icon icon={i.ico} className={"ico-" + i.ico} />;
}
var firstMenu = i.fstmenu ? "first-menu" : "";
var secondMenu = i.sndtitle ? "second-menu" : "";
var thirdMenu = i.trdtitle ? "third-menu" : "";
var classMn = i.fsttitle ? `mn-${i.fsttitle}` : "";
var classSb = i.sndtitle ? `sb-${i.sndtitle}` : "";
var classTm = i.trdtitle ? `tr-${i.trdtitle}`.replace(/ /g, "") : "";
return (
<React.Fragment key={'fragment'+ i.fsttitle + i.sndtitle + i.trdtitle}>
<li className={`list__item ${firstMenu}${secondMenu}${thirdMenu}`} key={i.fsttitle + i.sndtitle + i.trdtitle}>
<a
href={i.URL}
className={`${classMn}${classSb}${classTm}` + (this.state.showfstmenu === i.fsttitle ? ' active' : '')}
onClick={(e) => i.fstmenu ? this.showHideSubmenu(i.fsttitle) : null || i.trdtitle ? this.props.openTabs(e, i.URL, i.Iframe, i.trdtitle) : null }>
{icoJson}
<span>{i.fsttitle}{i.sndtitle}{i.trdtitle}</span>
</a>
{i.menu && renderMenu(i.menu)}
{this.state.showfstmenu === i.fsttitle && (
<>{i.fstmenu && renderMenu(i.fstmenu)}</>
)}
{i.sndmenu && renderMenu(i.sndmenu)}
</li>
{( this.state.showdesplegar) && (i.sndmenu) && (
<div key={"key"+index} className="content-bnt">
<button key={"ds"+index} id="desplegar" className="btn btn--rounded" onClick={this.desplegarClick}>
<Icon key={"arr"+index} icon="flecha" className="ico-flecha"/>
<Icon key={"fc"+index} icon="flecha" className="ico-flecha"/>
</button>
</div>
)}
</React.Fragment>
)
})}
</ul>
)
}
return (
<nav className={"nav" +( this.state.showdesplegar ? ' max-width' : '')}>
<div className="menu">
{renderMenu(this.props.navigation.menu)}
</div>
</nav>
)
}
}
You can use setTimeout() which executes logic after a certain period of time. We can use it in combination with componentDidUpdate(). We will check if the menu is open, in other words when showdesplegar: true and set it to false after a minute. Additionally, we need to bind a timer variable to set and clear the timer when the state changes, we call it this.timer
See sandbox for reference: https://codesandbox.io/s/sharp-sutherland-07d24
class Nav extends Component {
constructor(props){
super(props);
["desplegarClick",].forEach((method) => {
this[method] = this[method].bind(this);
});
this.state = {
addClassMenu: false,
addClassNav: false,
showtabs: this.props.showtabs
}
this.timer = null
}
componentDidUpdate() {
if (this.state.showdesplegar) {
this.timer = setTimeout(() => {
this.setState({
display: false
});
}, 60000);
} else {
clearTimeout(this.timer);
}
}
showHideSubmenu(index){
this.setState({
showfstmenu: index,
showdesplegar: true,
});
}
desplegarClick(){
this.setState({
showfstmenu: false,
showdesplegar: false,
});
}
render(){
const renderMenu = items => {
return (
<ul className="list">
{items.map((i, index) => {
var icoJson;
if(i.ico){
icoJson = <Icon icon={i.ico} className={"ico-" + i.ico} />;
}
var firstMenu = i.fstmenu ? "first-menu" : "";
var secondMenu = i.sndtitle ? "second-menu" : "";
var thirdMenu = i.trdtitle ? "third-menu" : "";
var classMn = i.fsttitle ? `mn-${i.fsttitle}` : "";
var classSb = i.sndtitle ? `sb-${i.sndtitle}` : "";
var classTm = i.trdtitle ? `tr-${i.trdtitle}`.replace(/ /g, "") : "";
return (
<React.Fragment key={'fragment'+ i.fsttitle + i.sndtitle + i.trdtitle}>
<li className={`list__item ${firstMenu}${secondMenu}${thirdMenu}`} key={i.fsttitle + i.sndtitle + i.trdtitle}>
<a
href={i.URL}
className={`${classMn}${classSb}${classTm}` + (this.state.showfstmenu === i.fsttitle ? ' active' : '')}
onClick={(e) => i.fstmenu ? this.showHideSubmenu(i.fsttitle) : null || i.trdtitle ? this.props.openTabs(e, i.URL, i.Iframe, i.trdtitle) : null }>
{icoJson}
<span>{i.fsttitle}{i.sndtitle}{i.trdtitle}</span>
</a>
{i.menu && renderMenu(i.menu)}
{this.state.showfstmenu === i.fsttitle && (
<>{i.fstmenu && renderMenu(i.fstmenu)}</>
)}
{i.sndmenu && renderMenu(i.sndmenu)}
</li>
{( this.state.showdesplegar) && (i.sndmenu) && (
<div key={"key"+index} className="content-bnt">
<button key={"ds"+index} id="desplegar" className="btn btn--rounded" onClick={this.desplegarClick}>
<Icon key={"arr"+index} icon="flecha" className="ico-flecha"/>
<Icon key={"fc"+index} icon="flecha" className="ico-flecha"/>
</button>
</div>
)}
</React.Fragment>
)
})}
</ul>
)
}
return (
<nav className={"nav" +( this.state.showdesplegar ? ' max-width' : '')}>
<div className="menu">
{renderMenu(this.props.navigation.menu)}
</div>
</nav>
)
}
}

Highlight row in one react table is highlighting the same row in a second one on the page

I have two react tables on the same page with different data. I have the option for the user to select multiple rows in both tables. The problem I am experiencing, is that if they select any row in one table, it selects the same row in the other table. How do I make it so on select it only styles the table that was selected?
Here is the getTrProps for the first table.
<ReactTable
data={this.state.table1state}
columns={columnNames}
showPagination={false}
minRows={0}
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
if (this.state.selectedOpp.indexOf(rowInfo.index) >= 0) {
var selectedOpp = this.state.selectedOpp;
selectedOpp.splice(selectedOpp.indexOf(rowInfo.index), 1);
this.setState({ selectedOpp: selectedOpp });
} else {
var selectedOpp = this.state.selected;
selectedOpp.push(rowInfo.index);
this.setState({ selectedOpp: selectedOpp });
}
},
style: {
background: this.state.selectedOpp.indexOf(rowInfo.index) >= 0 ? '#00afec' : 'white',
color: this.state.selectedOpp.indexOf(rowInfo.index) >= 0 ? 'white' : 'black'
}
}
} else {
return {}
}
}}
/>
And here is for the second. Exactly the same except I am using a different state to store the selected rows in, which doesn't fix the problem.
<ReactTable
data={this.state.table2state}
columns={columnNames}
showPagination={false}
minRows={0}
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
if (this.state.selected.indexOf(rowInfo.index) >= 0) {
var selected = this.state.selected;
selected.splice(selected.indexOf(rowInfo.index), 1);
this.setState({ selected: selected });
} else {
var selected = this.state.selected;
selected.push(rowInfo.index);
this.setState({ selected: selected });
}
},
style: {
background: this.state.selected.indexOf(rowInfo.index) >= 0 ? '#00afec' : 'white',
color: this.state.selected.indexOf(rowInfo.index) >= 0 ? 'white' : 'black'
}
}
} else {
return {}
}
}}
/>
Any way to specify more, like only style this specific table that this row is part of rather than that row index of all tables?
Thanks.
They are mapped to the same state of their container component. You might be passing different state variables for data but the selected state variable is the same for both React tables and so with the context being bound in the arrow functions, it's using the same parent state variable selected.
Try having different selected arrays for both the React tables.
Also, reduce code redundancy
<ReactTable
key="table1"
data={this.state.table2state}
columns={columnNames}
showPagination={false}
minRows={0}
getTrProps={(state, rowInfo) => {
const selected = this.state.selectedTable1;
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
if (this.state.selectedTable1.indexOf(rowInfo.index) >= 0) {
selected.splice(selected.indexOf(rowInfo.index), 1);
} else {
selected.push(rowInfo.index);
}
this.setState({ selectedTable1: selected });
},
style: {
background: this.state.selectedTable1.indexOf(rowInfo.index) >= 0 ? '#00afec' : 'white',
color: this.state.selectedTable1.indexOf(rowInfo.index) >= 0 ? 'white' : 'black'
}
}
} else {
return {}
}
}
}
/>
Ended up fixing this by not using the row index (rowInfo.index). Since the indexes are shared between tables, seemed to highlight both. So, instead I used the _id of the row data as follows:
<ReactTable
data={this.state.table2state}
columns={columnNames}
showPagination={false}
minRows={0}
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
if (this.state.selected.indexOf(rowInfo.original._id) >= 0) {
var selected = this.state.selected;
selected.splice(selected.indexOf(rowInfo.original._id), 1);
this.setState({ selected: selected });
} else {
var selected = this.state.selected;
selected.push(rowInfo.original._id);
this.setState({ selected: selected });
}
},
style: {
background: this.state.selected.indexOf(rowInfo.original._id) >= 0 ? '#00afec' : 'white',
color: this.state.selected.indexOf(rowInfo.original._id) >= 0 ? 'white' : 'black'
}
}
} else {
return {}
}
}}
/>
This way each selection array has the id of the data in the row instead of just which row it is. Idk if you even need multiple selection arrays, prob dont, but still prob good to have them.

Categories