6

Using react-table, I can filter the data really well, but would like it to be more dynamic

Now what I'm struggling to complete is filtering the data in the graph, so that it's more meaningful, partly by clumping products from singular brands together in one bar chart, and also by removing excess amounts of brands that make the information harder to understand.

This is the Table component I am using

import React from 'react'
import {
    useTable,
    usePagination,
    useSortBy,
    useFilters,
    useGroupBy,
    useExpanded,
    useRowSelect,
  } from 'react-table'
  import matchSorter from 'match-sorter'
  
  // import makeData from '../example/makedata.js'
  
  
  // 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
    editable,
  }) => {
    // 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])
  
    if (!editable) {
      return `${initialValue}`
    }
  
    return <input value={value} onChange={onChange} onBlur={onBlur} />
  }
  
  // Define a default UI for filtering
  function DefaultColumnFilter({
    column: { filterValue, preFilteredRows, setFilter },
  }) {
    const count = preFilteredRows.length
  
    return (
      <input
        value={filterValue || ''}
        onChange={e => {
          setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
        }}
        placeholder={`Search ${count} records...`}
      />
    )
  }
  
  // This is a custom filter UI for selecting
  // a unique option from a list
  function SelectColumnFilter({
    column: { filterValue, setFilter, preFilteredRows, id },
  }) {
    // Calculate the options for filtering
    // using the preFilteredRows
    const options = React.useMemo(() => {
      const options = new Set()
      preFilteredRows.forEach(row => {
        options.add(row.values[id])
      })
      return [...options.values()]
    }, [id, preFilteredRows])
  
    // Render a multi-select box
    return (
      <select
        value={filterValue}
        onChange={e => {
          setFilter(e.target.value || undefined)
        }}
      >
        <option value="">All</option>
        {options.map((option, i) => (
          <option key={i} value={option}>
            {option}
          </option>
        ))}
      </select>
    )
  }
  
  // This is a custom filter UI that uses a
  // slider to set the filter value between a column's
  // min and max values
  function SliderColumnFilter({
    column: { filterValue, setFilter, preFilteredRows, id },
  }) {
    // Calculate the min and max
    // using the preFilteredRows
  
    const [min, max] = React.useMemo(() => {
      let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      preFilteredRows.forEach(row => {
        min = Math.min(row.values[id], min)
        max = Math.max(row.values[id], max)
      })
      return [min, max]
    }, [id, preFilteredRows])
  
    return (
      <>
        <input
          type="range"
          min={min}
          max={max}
          value={filterValue || min}
          onChange={e => {
            setFilter(parseInt(e.target.value, 10))
          }}
        />
        <button onClick={() => setFilter(undefined)}>Off</button>
      </>
    )
  }
  
  // This is a custom UI for our 'between' or number range
  // filter. It uses two number boxes and filters rows to
  // ones that have values between the two
  function NumberRangeColumnFilter({
    column: { filterValue = [], preFilteredRows, setFilter, id },
  }) {
    const [min, max] = React.useMemo(() => {
      let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      preFilteredRows.forEach(row => {
        min = Math.min(row.values[id], min)
        max = Math.max(row.values[id], max)
      })
      return [min, max]
    }, [id, preFilteredRows])
  
    return (
      <div
        style={{
          display: 'flex',
        }}
      >
        <input
          value={filterValue[0] || ''}
          type="number"
          onChange={e => {
            const val = e.target.value
            setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
          }}
          placeholder={`Min (${min})`}
          style={{
            width: '70px',
            marginRight: '0.5rem',
          }}
        />
        to
        <input
          value={filterValue[1] || ''}
          type="number"
          onChange={e => {
            const val = e.target.value
            setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
          }}
          placeholder={`Max (${max})`}
          style={{
            width: '70px',
            marginLeft: '0.5rem',
          }}
        />
      </div>
    )
  }
  
  function fuzzyTextFilterFn(rows, id, filterValue) {
    return matchSorter(rows, filterValue, { keys: [row => row.values[id]] })
  }
  
  // Let the table remove the filter if the string is empty
  fuzzyTextFilterFn.autoRemove = val => !val
  
  // Be sure to pass our updateMyData and the skipReset option
  function Table({ columns, data, updateMyData, skipReset }) {
    const td_header = "px-4 py-2"
  const td_style = "border px-4 py-2"
    const filterTypes = React.useMemo(
      () => ({
        // Add a new fuzzyTextFilterFn filter type.
        fuzzyText: fuzzyTextFilterFn,
        // Or, override the default text filter to use
        // "startWith"
        text: (rows, id, filterValue) => {
          return rows.filter(row => {
            const rowValue = row.values[id]
            return rowValue !== undefined
              ? String(rowValue)
                  .toLowerCase()
                  .startsWith(String(filterValue).toLowerCase())
              : true
          })
        },
      }),
      []
    )
  
    const defaultColumn = React.useMemo(
      () => ({
        // Let's set up our default Filter UI
        Filter: DefaultColumnFilter,
        // And also our default editable cell
        Cell: EditableCell,
      }),
      []
    )
  
    // Use the state and functions returned from useTable to build your UI
    const {
      getTableProps,
      getTableBodyProps,
      headerGroups,
      prepareRow,
      page, // Instead of using 'rows', we'll use page,
      // which has only the rows for the active page
  
      // The rest of these things are super handy, too ;)
      canPreviousPage,
      canNextPage,
      pageOptions,
      pageCount,
      gotoPage,
      nextPage,
      previousPage,
      setPageSize,
      state: {
        pageIndex,
        pageSize,
        sortBy,
        groupBy,
        expanded,
        filters,
        selectedRowIds,
      },
    } = useTable(
      {
        columns,
        data,
        defaultColumn,
        filterTypes,
        // 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,
        // We also need to pass this so the page doesn't change
        // when we edit the data.
        autoResetPage: !skipReset,
        autoResetSelectedRows: !skipReset,
        disableMultiSort: true,
      },
      useFilters,
      useGroupBy,
      useSortBy,
      useExpanded,
      usePagination,
      useRowSelect,
      // Here we will use a plugin to add our selection column
      hooks => {
        hooks.visibleColumns.push(columns => {
          return [
            {
              id: 'selection',
              // Make this column a groupByBoundary. This ensures that groupBy columns
              // are placed after it
              groupByBoundary: true,
              // The header can use the table's getToggleAllRowsSelectedProps method
              // to render a checkbox
              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,
          ]
        })
      }
    )
  
    // Render the UI for your table
    return (
      <>
        <table className="w-full text-md bg-white shadow-md rounded mb-4" {...getTableProps()}>
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th className={td_header} {...column.getHeaderProps()}>
                    <div>
                      {column.canGroupBy ? (
                        // If the column can be grouped, let's add a toggle
                        <span {...column.getGroupByToggleProps()}>
                          {column.isGrouped ? 'Click to Un-Group  Click to Sort!' : ' Click to Group  Click to Sort!'}
                        </span>
                      ) : null}
                      <span {...column.getSortByToggleProps()}>
                        {column.render('Header')}
                        {/* Add a sort direction indicator */}
                        {column.isSorted
                          ? column.isSortedDesc
                            ? ' '
                            : ' '
                          : ''}
                      </span>
                    </div>
                    {/* Render the columns filter UI */}
                    <div>{column.canFilter ? column.render('Filter') : null}</div>
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {page.map(row => {
              prepareRow(row)
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td className = {td_style} {...cell.getCellProps()}>
                        {cell.isGrouped ? (
                          // If it's a grouped cell, add an expander and row count
                          <>
                            <span {...row.getToggleRowExpandedProps()}>
                              {row.isExpanded ? '' : ''}
                            </span>{' '}
                            {cell.render('Cell', { editable: false })} (
                            {row.subRows.length})
                          </>
                        ) : cell.isAggregated ? (
                          // If the cell is aggregated, use the Aggregated
                          // renderer for cell
                          cell.render('Aggregated')
                        ) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
                          // Otherwise, just render the regular cell
                          cell.render('Cell', { editable: true })
                        )}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </table>
        {/*
          Pagination can be built however you'd like.
          This is just a very basic UI implementation:
        */}
        <div className="pagination">
          <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
            {'<<'}
          </button>{' '}
          <button onClick={() => previousPage()} disabled={!canPreviousPage}>
            {'<'}
          </button>{' '}
          <button onClick={() => nextPage()} disabled={!canNextPage}>
            {'>'}
          </button>{' '}
          <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
            {'>>'}
          </button>{' '}
          <span>
            Page{' '}
            <strong>
              {pageIndex + 1} of {pageOptions.length}
            </strong>{' '}
          </span>
          <span>
            | Go to page:{' '}
            <input
              type="number"
              defaultValue={pageIndex + 1}
              onChange={e => {
                const page = e.target.value ? Number(e.target.value) - 1 : 0
                gotoPage(page)
              }}
              style={{ width: '100px' }}
            />
          </span>{' '}
          <select
            value={pageSize}
            onChange={e => {
              setPageSize(Number(e.target.value))
            }}
          >
            {[10, 20, 30, 40, 50].map(pageSize => (
              <option key={pageSize} value={pageSize}>
                Show {pageSize}
              </option>
            ))}
          </select>
        </div>
        <pre>
          {/* <code>
            {JSON.stringify(
              {
                pageIndex,
                pageSize,
                pageCount,
                canNextPage,
                canPreviousPage,
                sortBy,
                groupBy,
                expanded: expanded,
                filters,
                selectedRowIds: selectedRowIds,
              },
              null,
              2
            )}
          </code> */}
        </pre>
      </>
    )
  }
  
  // Define a custom filter filter function!
  function filterGreaterThan(rows, id, filterValue) {
    return rows.filter(row => {
      const rowValue = row.values[id]
      return rowValue >= filterValue
    })
  }
  
  // This is an autoRemove method on the filter function that
  // when given the new filter value and returns true, the filter
  // will be automatically removed. Normally this is just an undefined
  // check, but here, we want to remove the filter if it's not a number
  filterGreaterThan.autoRemove = val => typeof val !== 'number'
  
  // This is a custom aggregator that
  // takes in an array of leaf values and
  // returns the rounded median
  function roundedMedian(leafValues) {
    let min = leafValues[0] || 0
    let max = leafValues[0] || 0
  
    leafValues.forEach(value => {
      min = Math.min(min, value)
      max = Math.max(max, value)
    })
  
    return Math.round((min + max) / 2)
  }
  
  const IndeterminateCheckbox = React.forwardRef(
    ({ indeterminate, ...rest }, ref) => {
      const defaultRef = React.useRef()
      const resolvedRef = ref || defaultRef
  
      React.useEffect(() => {
        resolvedRef.current.indeterminate = indeterminate
      }, [resolvedRef, indeterminate])
  
      return (
        <>
          <input type="checkbox" ref={resolvedRef} {...rest} />
        </>
      )
    }
  )
  
 
  
  export default Table

I would love some help in having the react-table filtered data being updated into the graph component above, dynamically or on a button push, and having the initial call be filtered to make it more usable.

Update

Brilliant code below, I am now getting this error:

Uncaught TypeError: Cannot read property 'Price' of undefined
    at stackValue (stack.js:7)
    at stack (stack.js:26)
    at generateVerticalStackedBars (nivo-bar.esm.js:212)
    at generateStackedBars (nivo-bar.esm.js:343)
    at Bar (nivo-bar.esm.js:809)
    at renderWithHooks (react-dom.development.js:14803)
    at updateFunctionComponent (react-dom.development.js:17034)
    at beginWork (react-dom.development.js:18610)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at beginWork$1 (react-dom.development.js:23203)
    at performUnitOfWork (react-dom.development.js:22154)
    at workLoopSync (react-dom.development.js:22130)
    at performSyncWorkOnRoot (react-dom.development.js:21756)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at discreteUpdates$1 (react-dom.development.js:21893)
    at discreteUpdates (react-dom.development.js:806)
    at dispatchDiscreteEvent (react-dom.development.js:4168)
index.js:1 The above error occurred in the <Bar> component:
    in Bar (created by pure(Bar))
    in pure(Bar) (created by withPropsOnChange(pure(Bar)))
    in withPropsOnChange(pure(Bar)) (created by withPropsOnChange(withPropsOnChange(pure(Bar))))
    in withPropsOnChange(withPropsOnChange(pure(Bar))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))) (created by withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))))
    in withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))) (created by defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))))))
    in defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))))) (created by withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))))))
    in withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))))) (created by defaultProps(withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))))))))
    in defaultProps(withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar)))))))))))) (created by withPropsOnChange(defaultProps(withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))))))))
    in withPropsOnChange(defaultProps(withPropsOnChange(defaultProps(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(withPropsOnChange(pure(Bar))))))))))))) (created by Bar)
    in Bar (at Chart.js:6)
    in div (at Chart.js:5)
    in Chart (at smartSpy.tsx:276)
    in div (at smartSpy.tsx:275)
    in div (at smartSpy.tsx:274)
    in div (at smartSpy.tsx:273)
    in div (at smartSpy.tsx:222)
    in div (at LayoutExampleEcommerce.js:26)
    in div (at LayoutExampleEcommerce.js:21)
    in div (at LayoutExampleEcommerce.js:14)
    in Layout (at smartSpy.tsx:220)
    in SmartSpy (at _app.js:8)
    in MyApp
    in Container (created by AppContainer)
    in AppContainer

React will try to recreate this component tree from scratch using the error boundary you provided, MyApp.
console.<computed> @ index.js:1
_app.js:7 Uncaught TypeError: Cannot read property 'Price' of undefined
    at stackValue (stack.js:7)
    at stack (stack.js:26)
    at generateVerticalStackedBars (nivo-bar.esm.js:212)
    at generateStackedBars (nivo-bar.esm.js:343)
    at Bar (nivo-bar.esm.js:809)
    at renderWithHooks (react-dom.development.js:14803)
    at updateFunctionComponent (react-dom.development.js:17034)
    at beginWork (react-dom.development.js:18610)
    at HTMLUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at beginWork$1 (react-dom.development.js:23203)
    at performUnitOfWork (react-dom.development.js:22154)
    at workLoopSync (react-dom.development.js:22130)
    at performSyncWorkOnRoot (react-dom.development.js:21756)
    at react-dom.development.js:11089
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushSyncCallbackQueueImpl (react-dom.development.js:11084)
    at flushSyncCallbackQueue (react-dom.development.js:11072)
    at discreteUpdates$1 (react-dom.development.js:21893)
    at discreteUpdates (react-dom.development.js:806)
    at dispatchDiscreteEvent (react-dom.development.js:4168)

This is occuring when the data is grouped by the react-table function (changing the values of the rows completely, by grouping the clicked on column).

Thanks again

Not all linked together, but should make it easy to understand how to solve this issue.

Thanks so much!

Community
  • 1
  • 1
LeCoda
  • 538
  • 7
  • 36
  • 79

1 Answers1

4

What you can do is communicate between Table and Chart with the help of parent. With react table you can get the filtered data from rows. You can use that and pass on that information to the parent which in turn will pass the data to Chart

    import OriginalData from 'path/to/data';

    const App = () => {
       const [data, setData] = useState(OriginalData);
       const updateData = useCallback((data) => {
           setData(data);
       }, []);
    
       return (
           <>
              <Chart data={data} />
              <Table updateData={updateData} data={originalData}/>
           </>
       )
    }

Now in Table

const Table = ({data, updateData}) => {
    // Use the state and functions returned from useTable to build your UI
    ...
    const {
      getTableProps,
      getTableBodyProps,
      headerGroups,
      prepareRow,
      rows,
      page, // Instead of using 'rows', we'll use page,
      // which has only the rows for the active page

      // The rest of these things are super handy, too ;)
      canPreviousPage,
      canNextPage,
      pageOptions,
      pageCount,
      gotoPage,
      nextPage,
      previousPage,
      setPageSize,
      state: {
        pageIndex,
        pageSize,
        sortBy,
        groupBy,
        expanded,
        filters,
        selectedRowIds,
      },
    } = useTable(
      {
        columns,
        data,
        defaultColumn,
        filterTypes,
        // 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,
        // We also need to pass this so the page doesn't change
        // when we edit the data.
        autoResetPage: !skipReset,
        autoResetSelectedRows: !skipReset,
        disableMultiSort: true,
      },
      useFilters,
      useGroupBy,
      useSortBy,
      useExpanded,
      usePagination,
      useRowSelect,
      // Here we will use a plugin to add our selection column
      hooks => {
        hooks.visibleColumns.push(columns => {
          return [
            {
              id: 'selection',
              // Make this column a groupByBoundary. This ensures that groupBy columns
              // are placed after it
              groupByBoundary: true,
              // The header can use the table's getToggleAllRowsSelectedProps method
              // to render a checkbox
              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,
          ]
        })
      }
    )
 
    useEffect(() => {
        const newData = rows.map(r => r.original);
        updateData(newData); // update to parent based on change in row
    }, [rows])

    ...
}
Etienne Martin
  • 10,018
  • 3
  • 35
  • 47
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • Brilliant - Trying this ASAP, will respond if it works well, cheers @shubhamKhatri – LeCoda May 29 '20 at 10:35
  • Getting an infinite loop error now - I think the solution worked, but led to a non-working outcome unfortunately! – LeCoda May 31 '20 at 15:34
  • 1
    Make sure that the parent update of state doesn't re-render Table component, you can make use of React.memo for it. The issue could that due to re-rendering of Table component a new instance of row is being returned. Also I hope that you are passing a newReference of data to Table component everytime – Shubham Khatri May 31 '20 at 15:37
  • oh that makes sense - thanks so much Shubham, absolutely amazing code. – LeCoda May 31 '20 at 15:40
  • Glad to have helped Michael :-) – Shubham Khatri May 31 '20 at 15:41
  • Oh one other issue haha, When I group the elements, I get this issue,, will link in main – LeCoda May 31 '20 at 15:46
  • I assume this error is due to how significantly the row values are changing – LeCoda May 31 '20 at 15:48
  • Where is price used? – Shubham Khatri May 31 '20 at 15:49
  • BTW, you can use `filters` as a dependnecy to useEffect in child insteaf of `rows`. That will resolve your issue of multiple updates – Shubham Khatri May 31 '20 at 15:51
  • ^^ Hold up, the site isn't showing the updated code. It is filtering the graph in the output when i filter by price, but when i group it breaks – LeCoda May 31 '20 at 15:52
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/215035/discussion-between-shubham-khatri-and-michael-holborn). – Shubham Khatri May 31 '20 at 15:53
  • Please check the format of rows with groupBy and with filters. Once you do that process the response in useEffect accordingly to update the parent – Shubham Khatri Jun 01 '20 at 03:33