Last rows in inner table don't expand in Fixed Data Table - javascript

I use expanded rows in fixed-data-table-2 component. I have 3 levels on inner tables:
If I click collapse cells in inner table (second nested level), rows don't expand and last nested table is not rendered. It occurs after first click of parent row, but after second click the table is rendered.
What is more strange, this behaviour doesn't happen if
a) I click first three rows of second table or
b) if I expand first row in main(first) table
It happens with last rows of second table if other than first row of main table is expanded.
You can see this behaviour in this and this recording.
codesandbox
CollapseCell.jsx
import React from 'react';
import { Cell } from 'fixed-data-table-2';
const { StyleSheet, css } = require('aphrodite');
export default class CollapseCell extends React.PureComponent {
render() {
const {
data, rowIndex, columnKey, collapsedRows, callback, ...props
} = this.props;
return (
<Cell {...props} className={css(styles.collapseCell)}>
<a onClick={evt => callback(rowIndex)}>{collapsedRows.has(rowIndex) ? '\u25BC' : '\u25BA'}</a>
</Cell>
);
}
}
const styles = StyleSheet.create({
collapseCell: {
cursor: 'pointer',
},
});
TestMeet.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import isEmpty from 'lodash/isEmpty';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell.jsx';
import SecondInnerTable from './SecondInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class TestMeetView extends Component {
static propTypes = {};
state = {
tableData: [
{
start: '5/19',
end: '5/20',
host: 'DACA',
},
{
start: '6/15',
end: '6/15',
host: 'DACA',
},
{
start: '6/16',
end: '6/17',
host: 'DACA',
},
{
start: '7/15',
end: '7/16',
host: 'DACA',
},
{
start: '7/30',
end: '7/31',
host: 'DACA',
},
],
columnWidths: {
start: 200,
end: 200,
host: 200,
action: 100,
},
tableContainerWidth: 0,
numOfExpRows: 0,
expChildRows: {},
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ columnWidths }) => ({
columnWidths: {
...columnWidths,
[columnKey]: newColumnWidth,
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
this.setState({
tableContainerWidth: newTableContainerWidth,
columnWidths: {
start: newTableContainerWidth / 3,
end: newTableContainerWidth / 3,
host: newTableContainerWidth / 3,
},
});
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0,
},
});
}
};
subRowHeightGetter = (index) => {
const numExpChildRows = this.state.expChildRows[index] ? this.state.expChildRows[index] : 0;
return this.state.collapsedRows.has(index) ? 242 * (numExpChildRows + 1) + 50 : 0;
};
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<SecondInnerTable
changeNumOfExpandedRows={this.setNumOfInnerExpandedRows}
parentRowIndex={rowIndex}
/>
</div>
</div>
);
};
setNumOfInnerExpandedRows = (numOfExpandedRows, rowIndex) => {
this.setState({
expChildRows: {
...this.state.expChildRows,
[rowIndex]: numOfExpandedRows,
},
});
};
render() {
let sumOfExpChildRows = 0;
if (!isEmpty(this.state.expChildRows)) {
sumOfExpChildRows = Object.values(this.state.expChildRows).reduce((a, b) => a + b);
}
return (
<div className="test-view">
<div className="container-fluid">
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={40}
width={this.state.tableContainerWidth}
height={(this.state.numOfExpRows + sumOfExpChildRows + 1) * 242}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
<Column
columnKey="start"
header={<Cell>Start</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].start}</Cell>}
width={this.state.columnWidths.start}
isResizable
/>
<Column
columnKey="end"
header={<Cell>End</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].end}</Cell>}
width={this.state.columnWidths.end}
isResizable
/>
<Column
columnKey="host"
header={<Cell>Host</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].host}</Cell>}
width={this.state.columnWidths.host}
isResizable
/>
</Table>
</div>
</div>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '242px',
margin: '10px',
},
});
SecondInnerTable.jsx
import React, { Component } from 'react';
import { Table, Column, Cell } from 'fixed-data-table-2';
import debounce from 'lodash/debounce';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell';
import ThirdInnerTable from './ThirdInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class SecondInnerTable extends Component {
state = {
tableData: [
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
],
tableColumns: {
dateOfSession: { label: 'Date of Session', width: 150 },
timeline: { label: 'Timeline', width: 150 },
entries: { label: 'Entries', width: 150 },
},
tableContainerWidth: 0,
tableContainerHeight: 252,
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onSessionsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth - 20;
const newColumnsWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
dateOfSession: { label: tableColumns.dateOfSession.label, width: newColumnsWidth },
timeline: { label: tableColumns.timeline.label, width: newColumnsWidth },
entries: { label: tableColumns.entries.label, width: newColumnsWidth },
},
}));
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
} else {
numOfExpRows += 1;
}
this.setState(
{
tableContainerHeight: 252 * (numOfExpRows + 1),
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
},
() => {
this.props.changeNumOfExpandedRows(numOfExpRows, this.props.parentRowIndex);
},
);
};
subRowHeightGetter = index => (this.state.collapsedRows.has(index) ? 272 : 0);
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<ThirdInnerTable parentRowIndex={rowIndex} />
</div>
</div>
);
};
render() {
return (
<div className="mb-2" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={this.state.tableContainerHeight}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onSessionsTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
{Object.keys(this.state.tableColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '252px',
margin: '10px',
},
});
ThirdInnerTable.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
export default class ThirdInnerTable extends Component {
state = {
tableData: [
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
],
tableColumns: {
eventNumber: { label: 'Event number', width: 150 },
qualifyingTime: { label: 'Qualifying time', width: 150 },
selected: { label: 'Selected?', width: 150 },
},
tableContainerWidth: 0,
numOfColumns: 3,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onEventsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
const columnWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
eventNumber: { label: tableColumns.eventNumber.label, width: columnWidth },
qualifyingTime: { label: tableColumns.qualifyingTime.label, width: columnWidth },
selected: { label: tableColumns.selected.label, width: columnWidth },
},
}));
}
};
render() {
return (
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={252}
touchScrollEnabled
onColumnResizeEndCallback={this.onEventsTableColumnResizeEndCallback}
isColumnResizing={false}
>
{Object.keys(this.state.tableColumns).slice(0, this.state.numOfColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}

I have updated the Sandbox code, Kindly check the below link, I think it is useful for you
Code Sandbox
Or
handleCollapseClick = rowIndex => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
tableContainerHeight: 250 * numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0
}
});
}
};

I should have commented, but I don't have the required no. of points for that. Have you fixed the issue? Because I just opened the link you have attached and it's working fine
UPDATE
Your code works fine. The problem with your approach is that your click event is triggered on the arrow and not on your entire div (or cell). Therefore, you need to be on point and only click on the arrow in order for your row to expand. If you don't want this behavior, I'd suggest putting your click event on the div (or cell)
Here is a working video that demonstrates that it's working fine:
https://www.useloom.com/share/2c725f3fede942b09c661a765c72634e
I have taken a screenshot from your video which shows that because you're not clicking on the arrow, that's why your row is not expanding

Related

Uncaught Invariant Violation: Too many re-renders. React limits the number of renders to prevent an infinite loop in pagination

I have a data in a table and i want to add a pagination option to it. In the user interface, user should be able to choose data per page and page index that's why i added some handle methods to it and try to render it but i faced with the above error.
Whole jsx file is too long to share therefore i only shared codes which seemed important to me and replaced other codes with ... if you need anything else you can ask it in the comments.
function Activity(props) {
...
const variables = useMemo(() => ({
projectId,
language,
env: environment,
pageSize: 20,
filter,
...getSortFunction(),
}), [projectId, language, environment, filter, getSortFunction()]);
const {
data, hasNextPage, loading, loadMore, refetch,
} = useActivity(variables);
//pagination section
const [pagination, setPagination] = useState({
currentPage: 1,
dataPerPage: 10,
indexOfLastData: 9,
indexOfFirstData: 0,
})
const [totalPages, setTotalPages] = useState(1);
const [paginationString, setPaginationString] = useState(`Current page ${pagination.currentPage}, total number of data ${pagination.dataPerPage}`)
const handlePagination = (page, dataPerPage, currentPagination) => {
if (currentPagination.currentPage != page) handlePaginationCurrentPage(page, currentPagination);
if (currentPagination.dataPerPage != dataPerPage) handlePaginationDataPerPage(dataPerPage, currentPagination);
const dataToBeUsed = [...data].slice(pagination.indexOfFirstData, pagination.indexOfLastData);
setTotalPages(Math.ceil(3 / pagination.dataPerPage));
return dataToBeUsed;
}
const handlePaginationString = () => {
setPaginationString(`current page ${pagination.currentPage}, total number of data ${pagination.dataPerPage}`)
}
const handlePaginationCurrentPage = (page, pagination) => {
useEffect(() => {
setPagination({
...pagination,
currentPage: Number(page),
indexOfLastData: pagination.dataPerPage * Number(page) - 1,
indexOfFirstData: pagination.dataPerPage * Number(page) - pagination.dataPerPage
})
})
}
const handlePaginationDataPerPage = (dataPerPage, pagination) => {
useEffect(() => {
setPagination({
...pagination,
dataPerPage: dataPerPage,
indexOfLastData: dataPerPage * pagination.currentPage - 1,
indexOfFirstData: dataPerPage * pagination.currentPage - pagination.dataPerPage
})
})
}
const resetPagination = (e) => {
e.stopPropagation();
handlePagination(1, 10, pagination);
};
...
const renderActions = row => (
<ActivityActionsColumn
outdated={ isUtteranceOutdated(row.datum) }
datum={ row.datum }
handleSetValidated={ handleSetValidated }
onDelete={ handleDelete }
onMarkOoS={ handleMarkOoS }
data={ handlePagination(1, 10, pagination) }
getSmartTips={ utterance => getSmartTips({
nluThreshold, endTime, examples, utterance,
}) }
/>
);
...
const columns = [
{ key: '_id', selectionKey: true, hidden: true },
{
key: 'confidence',
style: { width: '51px', minWidth: '51px' },
render: renderConfidence,
},
{
key: 'intent',
style: { width: '180px', minWidth: '180px', overflow: 'hidden' },
render: renderIntent,
},
{
key: 'conversation-popup', style: { width: '30px', minWidth: '30px' }, render: renderConvPopup,
},
{
key: 'text',
style: { width: '100%' },
render: renderExample,
},
...(can('incoming:w', projectId) ? [
{
key: 'actions',
style: { width: '110px' },
render: renderActions,
},
] : []),
];
const renderTopBar = () => (
<div className='side-by-side wrap' style={ { marginBottom: '10px' } }>
...
<Accordion className='pagination-accordion'>
<Accordion.Title
active={ activeAccordion }
onClick={ () => handleAccordionClick() }
data-cy='toggle-pagination'
className='pagination-accordian-title'
>
<Icon name='dropdown' />
<span className='toggle-pagination'>
{ activeAccordion
? `Hide Pagination Options `
: `Show Pagination Options ` }
</span>
<span className="toggle-pagination pagination-string">
{ activeAccordion
? `${paginationString}`
: `${paginationString}` }
</span>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */ }
<span
data-cy='reset-pagination'
onClick={ e => resetPagination(e) }
role='button'
tabIndex='0'
className='reset-button'
>
<Icon name='redo' size='small' /> Reset
</span>
</Accordion.Title>
</Accordion>
</div>
);
return (
<>
{ !!openConvPopup && <ConversationSidePanel utterance={ openConvPopup } onClose={ () => setOpenConvPopup(false) } /> }
{ renderTopBar() }
{ data && data.length ? (
<>
<DataTable
ref={ tableRef }
columns={ columns }
data={ handlePagination(1, 10, pagination) }
hasNextPage={ hasNextPage }
loadMore={ loading ? () => { } : loadMore }
onScroll={ handleScroll }
selection={ selection }
onChangeSelection={ (newSelection) => {
setSelection(newSelection);
setOpenConvPopup(false);
} }
/>
)}
I can't use data in pagination beacuse it is used in so many places and in those places everything designed assumed data is in its full length so i should use it seperately (ex./ in handlePagination i get it using data.slice() function )
Thanks!
I changed
<DataTable
ref={ tableRef }
columns={ columns }
data={ handlePagination(1, 10, pagination) }
hasNextPage={ hasNextPage }
loadMore={ loading ? () => { } : loadMore }
onScroll={ handleScroll }
selection={ selection }
onChangeSelection={ (newSelection) => {
setSelection(newSelection);
setOpenConvPopup(false);
} }
/>
to
<DataTable
ref={ tableRef }
columns={ columns }
data={ dataToBeShowed }
hasNextPage={ hasNextPage }
loadMore={ loading ? () => { } : loadMore }
onScroll={ handleScroll }
selection={ selection }
onChangeSelection={ (newSelection) => {
setSelection(newSelection);
setOpenConvPopup(false);
} }
/>
and added
const [dataToBeShowed, setDataToBeShowed] = useState(data.slice(pagination.indexOfFirstData, pagination.indexOfLastData));
useEffect(() => {
setDataToBeShowed(data.slice(pagination.indexOfFirstData, pagination.indexOfLastData));
setPaginationString(`Current page ${pagination.currentPage}, total number of data ${pagination.dataPerPage}`)
setTotalPages(Math.ceil(data.length / pagination.dataPerPage));
}, [data, pagination.indexOfFirstData, pagination.indexOfLastData]);
This solved becuase I saw that handlePagination(1, 10, pagination) doesn't render in Activity but in Window which means It can't see the states, therefore i added dataToBeShown and assigned it to real data and since i added useEffect, it follows the change in data (real data).
(if you use onClick vs. you can simply add () => {handleOnBruh()} vs this way it binds itself with the file.)
Thanks!

Why my dynamic block throws: Uncaught SyntaxError: expected expression, got '<'?

I'm trying to make a slideshow dynamic block, actually, it alredy works, but, throws this error and I don't know why.
Uncaught SyntaxError: expected expression, got '<' slideshow-category-picker.js:33:14
This error appears twice at customizer and once at the public site, at the public site it doesn't cause performance issues, but, at the customizer it doesn't let me start any other script.
I hope somebody can help me.
/*slideshow-category-picker.js*/
const {
withSelect
} = wp.data;
const {
SelectControl
} = wp.components;
/* Guardar categorias en un arreglo */
const CategorySelect = (props) => {
const {
terms,
selectTerm
} = props;
const catSelections = [];
catSelections.push({
value: '',
label: 'Todas las categorías'
} );
terms &&(
terms.forEach(item => {
catSelections.push({
label: item.name,
value: item.id
} );
}));
return(
<SelectControl
className = "slideshow-categoryselect"
label = "Categorías"
options = {
catSelections
}
onChange = {
(itemId) => selectTerm(itemId)
}
/>
);
}
export default withSelect((select, props) => {
return {
terms: select('core').getEntityRecords('taxonomy', 'category', {
per_page: -1
})
}
})(CategorySelect);
/*index.js*/
const {
__
} = wp.i18n;
const {
registerBlockType
} = wp.blocks;
const {
Placeholder,
PanelBody,
RangeControl
} = wp.components;
const {
withSelect
} = wp.data;
const {
InspectorControls
} = wp.blockEditor;
import CategorySelect from './slideshow-category-picker';
const BlockEdit = (props) => {
const {
attributes,
setAttributes
} = props;
const selectTerm = (termId) => {
setAttributes({
termId: termId
});
}
return ( <div className = {props.className }>
<InspectorControls>
<PanelBody
title = { __('Slider Settings', 'slideshow') }
initialOpen = {true}
>
<RangeControl
label = { __('Cantidad de Diapositivas', 'slideshow') }
value = { attributes.numSlides }
onChange = {
(val) => setAttributes({
numSlides: val
})
}
min = { 1 }
max = { 10}
/>
<CategorySelect
selectedTermId = {
attributes.termId
}
selectTerm = {
selectTerm
}
/>
</PanelBody>
</InspectorControls>
<Placeholder label = { __('SlidesShow Posts', 'slideshow')}>
</Placeholder>
</div>
);
}
//Logo para el bloque
import {
ReactComponent as Logo
} from '../slideshow-icon.svg';
registerBlockType('slideshow/slides', {
title: __('Slideshow Posts', 'slideshow'),
icon: {
src: Logo
},
category: 'slideshow',
attributes: {
termId: {
type: 'number',
default: 0
},
numSlides: {
type: 'number',
default: 3
},
},
edit: BlockEdit,
save: () => {
return null;
}
});

Getting value of SetState not updated in react

I am having a callback function which updates my array in the SetState and it works fine as I can see in the console log in the handleDaysValueChange all is good. But when I try to access it in another function i.e handleValuesChange the values are not up to date. I am missing a async or something.
import React from 'react';
import PropTypes from 'prop-types';
import AppService from 'services/app-service';
import Enum from 'enum';
import { ApiData } from 'data';
import { isEmpty, boolValue } from 'core/type-check';
import { notifySuccess } from 'core/errors';
import { withDrawerForm } from 'components/_hoc';
import { ExtendedAvatar } from 'components/extended-antd';
import { Tabs } from 'antd';
import { FormTemplateAudit } from '../templates';
import ShiftRosterFormDetails from './shiftroster-form-details';
import Item from 'antd/lib/list/Item';
const { TabPane } = Tabs;
class ShiftRosterForm extends React.Component {
constructor(props) {
super(props);
this.formInputRef = React.createRef();
this.state = {
daysValue:[]
};
this.handleDaysValueChange = this.handleDaysValueChange.bind(this);
}
handleDaysValueChange(daysValues) {
this.setState({ daysValue: daysValues }, () => {
console.log("Data" + this.props.customData);
console.log("Hello World " + JSON.stringify(this.state.daysValue));
});
}
componentDidMount() {
// Update the parent form state with a reference to the
// formInputRef so we can call this later for validation
// before saving the data to the server.
const { onFormStateChange } = this.props;
onFormStateChange({ formInputRef: this.formInputRef.current });
}
//Shift Roster Detail
handleValuesChange = (props, changedValues, allValues) => {
// Update the parent form state with the changed item
// details and mark the form as dirty
const { itemData, onFormStateChange } = this.props;
console.log("Hey" + this.state.daysValue);
onFormStateChange({
isFormDirty: true,
itemData: {
...itemData,
...changedValues,
...this.state.daysValue
}
});
}
render() {
const { itemData, customData } = this.props;
const isSR = (!isEmpty(itemData) && itemData.id > 0);
return (
<Tabs className="bhp-tabs" defaultActiveKey="1" animated={false}>
<TabPane key="1" tab="Details">
<ShiftRosterFormDetails
ref={this.formInputRef}
dataSource={itemData}
onValuesChange={this.handleValuesChange}
handleDaysValueChange={this.handleDaysValueChange}
/>
</TabPane>
<TabPane key="2" tab="Audit" disabled={!isSR}>
<FormTemplateAudit
itemData={itemData}
/>
</TabPane>
</Tabs>
);
}
}
ShiftRosterForm.propTypes = {
itemId: PropTypes.number, // Passed in by the HOC. The loaded Shift Roster id
itemData: PropTypes.object, // Passed in by the HOC. The loaded Shift Roster data
customData: PropTypes.object, // Temporary store to hold the changed Shift Roster
isFormDirty: PropTypes.bool, // Passed in by the HOC. Flags if the parent form is dirty
isLoading: PropTypes.bool, // Passed in by the HOC. Flags if the parent form is loading
daysValue: PropTypes.object,
onFormStateChange: PropTypes.func // Passed in by the HOC. Callback to update the parent form state.
};
ShiftRosterForm.defaultProps = {
itemId: -1,
itemData: {},
customData: {},
isFormDirty: false,
isLoading: false,
daysValue: {},
onFormStateChange() { }
};
const ShiftRosterFormTitle = ({ data }) => {
const name = (!isEmpty(data) && data.id > 0) ? `${data.name}` : 'New Shift Roster';//`${data.name}`
return isEmpty(data)
? <ExtendedAvatar type="icon" size="large" />
: <span><ExtendedAvatar name={name} type="letter" size="large" />{name}</span>
}
const saveShiftRoster = (shiftrosterId, shiftroster, rosterdays) => {
return ApiData.saveShiftRoster(shiftrosterId, shiftroster, rosterdays)
.then(response => {
notifySuccess('Save Successful', 'Site Work Package saved successfully');
return response;
})
.catch(error => {
throw error;
});
}
const saveForm = (formState, setFormState) => {
const { isFormDirty, itemData, customData, formInputRef } = formState;
const typeName = "Dynamic";
const actualType = itemData.type;
let rosterdays = [];
if (actualType !== typeName) {
rosterdays = GetDaysForRoster(itemData);
console.log("My Values" + JSON.stringify(rosterdays));
}
const shiftRosterId = itemData.id;
const isExistingShiftRoster = shiftRosterId > 0;
return new Promise((resolve, reject) => {
if (isExistingShiftRoster && !isFormDirty) {
// No Changes
notifySuccess('Save Successful', 'Site Work Package saved successfully');
resolve(itemData);
}
else {
// Validate and Save
formInputRef.validateFields((error, values) => {
if (!error) {
// Form validated successfully.
// Save form changes
const shiftrosterRecord = saveShiftRoster(shiftRosterId, values, rosterdays);
resolve(shiftrosterRecord);
}
else {
// Form validation error.
// Return data as is.
resolve(itemData);
}
});
}
});
}
const GetDaysForRoster = (itemsData) => {
const result = [];
const keys = Object.keys(itemsData);
for (const k in keys) {
if (Number(k) == k) {
result[k] = itemsData[k]
}
}
return result.filter(function (el) { return el != null });
}
const WrappedShiftRosterForm = withDrawerForm({
containerClassName: 'bhp-equipment-type-form',
title: (record) => <ShiftRosterFormTitle data={record} />,
onLoad: (itemId, setFormState) => ApiData.getShiftRoster(itemId),
onSave: (formState, setFormState) => { return saveForm(formState, setFormState); },
canView: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Read] }),
canCreate: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Create] }),
canUpdate: () => AppService.hasAccess({ [Enum.SecurityModule.EquipmentTypeDetails]: [Enum.SecurityPermission.Update] })
})(ShiftRosterForm);
WrappedShiftRosterForm.propTypes = {
containerClassName: PropTypes.string,
itemId: PropTypes.number,
visible: PropTypes.bool,
onSave: PropTypes.func,
onClose: PropTypes.func
};
WrappedShiftRosterForm.defaultProps = {
containerClassName: null,
itemId: -1,
visible: false,
onSave() { },
onClose() { }
};
export default WrappedShiftRosterForm;
//ShiftRosterFormDetails
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { ApiData } from 'data';
import { Form, Input, Select, Button, space, InputNumber } from 'antd';
import ShiftDays from './shiftdays'
const ShiftRosterFormDetails = ({ form, dataSource, onValueChange, handleDaysValueChange }) => {
const { getFieldDecorator } = form;
const formLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 12 },
},
};
console.log("My datasource" + dataSource.shiftRosterDays);
//const daysRoster = dataSource.shiftRosterDays || [{ daysIn: 1, daysOut: 1, category: "Day Shift" }];
const [inputList, setInputList] = useState([{ daysIn: 1, daysOut: 1, category: "Day Shift" }]);
const [selectedType, setSelectedType] = useState(dataSource.type || 'Fixed Single');
const [isTotalDaysRequired, SetTotalDaysRequired] = useState(dataSource.type === 'Dynamic' ? true : false);
const [isTotalDaysRequiredMessage, setIsTotalDaysRequiredMessage] = useState(dataSource.type === 'Dynamic' ? 'Please enter the Total Days' : '');
const handleTypeChanged = (value, e) => {
setSelectedType(value);
if (value === "Dynamic") {
SetTotalDaysRequired(true);
setIsTotalDaysRequiredMessage('Please enter the Total Days');
}
if (value === "Fixed Single") {
if (inputList.length > 1) {
const list = [...inputList];
console.log("Total" + inputList.length);
list.splice(1, inputList.length);
setInputList(list);
console.log("My List" + JSON.stringify(list));
console.log("Input List" + JSON.stringify(inputList));
handleDaysValueChange(list);
}
}
else {
SetTotalDaysRequired(false);
setIsTotalDaysRequiredMessage('');
}
};
return (
<div className='bhp-equipment-type-form-details bhp-content-box-shadow'>
<Form {...formLayout}>
<Form.Item label='Name' hasFeedback>
{getFieldDecorator('name', {
initialValue: dataSource.name,
rules: [{
required: true,
message: 'Please enter the Name'
}],
})(
//disabled={dataSource.id > 0}
<Input placeholder='Name' />
)}
</Form.Item>
<Form.Item label='Status' hasFeedback>
{getFieldDecorator('status', {
initialValue: dataSource.status,
rules: [{
required: true,
message: 'Please enter the Status'
}],
})(
<Select>
<Select.Option value="Active">Active</Select.Option>
<Select.Option value="InActive">InActive</Select.Option>
</Select>
)}
</Form.Item>
<Form.Item label='Type' hasFeedback>
{getFieldDecorator('type', {
initialValue: dataSource.type || 'Fixed Single',
rules: [{
required: true,
message: 'Please select the Type'
}],
})(
<Select onChange={handleTypeChanged}>
<Select.Option value="Fixed Single">Fixed Single</Select.Option>
<Select.Option value="Fixed Multiple">Fixed Multiple</Select.Option>
<Select.Option value="Dynamic">Dynamic</Select.Option>
</Select>
)}
</Form.Item>
<Form.Item label='Total Days' hasFeedback style={selectedType === 'Dynamic' ? { display: '' } : { display: 'none' }}>
{getFieldDecorator('totaldays', {
initialValue: dataSource.totalDays,
rules: [{
required: isTotalDaysRequired,
message: isTotalDaysRequiredMessage
}],
})(
<InputNumber min={1} max={365} />
)}
</Form.Item>
<ShiftDays inputList={inputList} setInputList={setInputList} selectedType={selectedType} handleDaysValueChange={handleDaysValueChange} getFieldDecorator={getFieldDecorator} />
</Form>
</div>
)};
const onFieldsChange = (props, changedFields, allFields) => {
if (props.onFieldsChange) {
props.onFieldsChange(props, changedFields, allFields);
}
};
const onValuesChange = (props, changedValues, allValues) => {
if (props.onValuesChange) {
props.onValuesChange(props, changedValues, allValues);
}
};
ShiftRosterFormDetails.propTypes = {
form: PropTypes.object,
dataSource: PropTypes.object,
onFieldsChange: PropTypes.func,
onValuesChange: PropTypes.func
};
ShiftRosterFormDetails.defaultProps = {
form: {},
dataSource: {},
onFieldsChange() { },
onValuesChange() { }
};
export default Form.create({
onValuesChange,
onFieldsChange
})(ShiftRosterFormDetails);

highcharts typeerror this is not a function

I have a highcharts function that populates a chart. It also has a drilldown event that needs to be called when the drilldown event is triggered.
I get this following error when I drilldown the charts-
TypeError: this.filter_data is not a function
The drilldown event is in the populate_group_by_gender_chart function under the options/chart/events.
import React, { Component } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Container, } from 'react-bootstrap';
import Header from './Components/Header';
import Wrapper from './Components/Wrapper';
import _ from 'lodash';
import './App.css';
let county_data = 'https://data.m.gov/api/views/kdqy-4wzv/rows.json?accessType=DOWNLOAD'
class App extends Component {
constructor(props){
super(props)
this.state = {
raw_data: [],
employee_data: [],
column_names: [],
page_num: 1,
group_by_gender_data: {}
}
}
componentDidMount = () => {
this.fetchData();
}
fetchData = async () => {
fetch(county_data).
then(response => response.json()).
then(response => {
// console.log(response.data)
let column_names = _.map(response.meta.view.columns, 'name')
const data_map = response.data.map(data=> {
return {
id: data[0],
'full_name':data[8],
'gender':data[9],
'current_annual_salary':data[10],
'2018_gross_pay_received':data[11],
'2018_overtime_pay':data[12],
'department':data[13],
'department_name':data[14],
'division':data[15],
'assignment_category':data[16],
'employee_position_title':data[17],
'position_under_filled':data[18],
'date_first_hired':data[19]
}
})
// console.log('data_map - ', data_map)
let grouped_data_by_chunks = _.chunk(data_map, data_map.length/100)
// console.log('grouped_data -', grouped_data_by_chunks)
this.setState({
raw_data: data_map,
employee_data: grouped_data_by_chunks[0],
column_names: column_names
})
this.populate_group_by_gender_chart(data_map);
})
}
populate_group_by_gender_chart = (data) => {
console.log('drilldown this.filter_data -', this.filter_data)
var options = {
chart: {
type: "pie",
events:{
drilldown: function(e){
console.log('e.point.name - ', e.point.name)
var filter_by = (e.point.name = 'Female') ? 'F': 'M'
this.filter_data('GENDER', data, filter_by)
}
}
},
title: {
text: 'Employees by Gender'
},
series: [
{
name: 'Gender',
data: []
}
],
plotOptions: {
series: {
cursor: 'pointer',
point: {}
},
},
};
options.series[0].data = this.filter_data('GENDER', data)
this.setState({
group_by_gender_options: options
})
}
filter_data = (filter, data, filter_by) => {
if (filter == 'GENDER'){
data = _.map(data, 'gender')
data = _.filter(data, function(val){
return val == 'F';
})
const result = _.values(_.groupBy(data)).map(d => ({'y' : d.length,
'name' : d[0] == 'F' ? 'Female': 'Male',
'id': d[0] == 'F' ? 'F': 'M',
'drilldown': true
}));
return result
}
}
render() {
return (
<div className="App">
<Container fluid={true}>
<Header/>
<Wrapper data={this.state.employee_data}
group_by_gender_chart_data={this.state.group_by_gender_options}
></Wrapper>
</Container>
</div>
);
}
}
export default App;

Updating nested objects in nested arrays in React

Expected effect:
click button -> call function save -> pass object p to function update
update second object{a: 'purple', desc: 'grt', date: '12 -10-2019 '} in colors array, which is in theproducts array
Before update: {a: 'purple', desc: 'grt', date: '12 -10-2019 '}
After update: {a: 'violet', desc: 'gt', date: '12 -12-1980 '}
Error in console.log:
Uncaught TypeError: this.props.product.colors.map is not a function
App
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}]
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a: 'white', {a:'black', desc: 'grtrt', date: '12-12-2119'}, }, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0 //It is first object in products array
index: 1 //It is second object in colors array
}
}
update = (item) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: item}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
Items
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
</div>
);
}
}
Item
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
You need to pass the index of the colors item and then update it accordingly
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<button onClick={this.save}>Save</button>
</div>
)
}
}
and then in the topmost parent
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorsIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
Working demo
const { Component } = React;
class App extends Component {
constructor (props) {
super(props);
this.state = {
products: [
{
colors: [{a:'orange', desc: 'grtrt', date: '02-12-2019'}, {a:'purple', desc: 'grt', date: '12-10-2019'}],
desc: 'gfgfg',
},
{
colors: [{a:'black', desc: 'g', date: '12-12-2019'}, {a:'black', desc: 'grtrt', date: '12-12-2119'}, {a:'gray', desc:'', date: '01-01-2000'}],
desc: 'gfgfgfg',
}
],
selectProductIndex: 0,
index: 1
}
}
update = (item, colorIndex) => {
const {selectProductIndex} = this.state;
this.setState(prevState => {
return {
products: [
...prevState.products.slice(0, selectProductIndex),
Object.assign({}, prevState.products[selectProductIndex], {colors: prevState.products[selectProductIndex].colors.map((it,idx) => {
if(idx === colorIndex) { return item}
return it;
})}),
...prevState.products.slice(selectProductIndex + 1)
]
};
});
}
render () {
return (
<div>
<Items
product={this.state.products[this.state.selectProductIndex]}
update = {this.update}
/>
</div>
)
}
}
class Items extends Component {
render () {
return (
<ul>
{
this.props.product.colors
.map((item, index) =>
<Item
key= {index}
index = {index}
time = {item}
update = {this.props.update}
/>
)
}
</ul>
);
}
}
class Item extends Component {
save = () => {
const p = {
a:'violet', desc: 'gt', date: '12-12-1980'
}
this.props.update(p, this.props.index)
}
render() {
return (
<div>
<pre>{JSON.stringify(this.props.time)}</pre>
<button onClick={this.save}>Save</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'));
<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>
<div id="app" />

Categories

Resources