mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.16k stars 1.3k forks source link

Support key 66250: Scroll position goes to the top in MUI data grid pro infinite scroll when loading more data. #9042

Closed adityapatil9191 closed 1 year ago

adityapatil9191 commented 1 year ago

Duplicates

Latest version

Steps to reproduce 🕹

Link to live example:

Steps:

  1. Integrated MUI Datagrid 2.Load data infinitely with MUI datagrid pro from firebase.
  2. The scroll position goes to the top everytime I load a new data.

Current behavior 😯

Scroll position goes to the top every time a load additional rows from the firebase .

Expected behavior 🤔

Scroll position should not go to the top. Below is the reference code snippet.

import * as React from 'react'; import { DataGridPro, GridSortModel } from '@mui/x-data-grid-pro'; import { where } from 'firebase/firestore'; import LinearProgress from '@mui/material/LinearProgress'; import { db } from '@/app/firebase/config'; import { useContext, useEffect, useState } from 'react'; // Remove this line import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; import AddRowButton from '../../global/IconAndButton'; import AddIcon from '@mui/icons-material/Add';

import { collection, getDocs, limit, orderBy, query, startAfter, } from 'firebase/firestore'; import { AvatarContext } from '@/app/contexts/avatar.context'; import GridChipsWithDropDown from '../../global/GridChips';

const addIcon = ( <AddIcon sx={{ color: '#6626E9', font: 'normal normal normal 16px/24px Font Awesome 6 Pro', }} /> );

const MuiDataGrid = () => { const handleAddRow = () => { const newRow = { id: rows.length + 1, prompt: '', completion: '', label: [], }; setRows([...rows, newRow]); }; const [loading, setLoading] = useState(true); const [rows, setRows] = useState([]); const [lastDoc, setLastDoc] = useState(null); const { avatar } = useContext(AvatarContext); const [queryOptions, setQueryOptions] = React.useState({ sortModel: [], }); const columns = [ { field: 'prompt', headerName: 'PROMPT', flex: 1, editable: true, headerClassName: 'cva-ai-grid-theme', }, { field: 'completion', headerName: 'COMPLETION', flex: 1, editable: true, headerClassName: 'cva-ai-grid-theme', }, { field: 'labels', headerName: 'LABELS', flex: 1, sortable: false, headerClassName: 'cva-ai-grid-theme', renderCell: (params: any) => ( <GridChipsWithDropDown handleKeyDownEvent={(event) => handleCellKeyDown(params, event)} labels={params.row.label} /> ), }, ];

const createQueryBasedOnModels = (sortModel: GridSortModel) => { let q;

if (sortModel && sortModel.length && sortModel[0].field) {
  q = query(
    collection(db, 'personalities', avatar.id, 'prompts_completion'),
    where(sortModel[0].field, '>=', ''),
    orderBy(sortModel[0].field, sortModel[0].sort),
    limit(25)
  );
} else {
  q = query(
    collection(db, 'personalities', avatar.id, 'prompts_completion'),
    orderBy('index'),
    limit(25)
  );
}
if (lastDoc) {
  q = query(q, startAfter(lastDoc));
}
return q;

};

const handleSortModelChange = async (sortModel: GridSortModel) => { setQueryOptions((prevState) => { return { sortModel: [...sortModel], }; }); if (queryOptions.sortModel && queryOptions.sortModel.length) { setLoading(true); const q = createQueryBasedOnModels(sortModel); const querySnapshot = await getDocs(q); const newRows = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setRows([...newRows]); setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]); setLoading(false); } };

const handleCellKeyDown = (params, event) => { if (event.key === ' ') { event.stopPropagation(); } };

const handleLoadMore = async (sortModel: GridSortModel) => { if (lastDoc) { setLoading(true); const q = createQueryBasedOnModels(sortModel); const querySnapshot = await getDocs(q);

  const newRows = querySnapshot.docs.map((doc) => ({
    id: doc.id,
    ...doc.data(),
  }));
  setRows([...rows, ...newRows]);
  setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]);
  setLoading(false);
} else {
  return;
}

};

useEffect(() => { const fetchData = async () => { setLoading(true); const q = query( collection(db, 'personalities', avatar.id, 'prompts_completion'), // where('field', '==', 'value'), orderBy('index'), limit(25) ); const querySnapshot = await getDocs(q); const newRows = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setRows(newRows); setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]); setLoading(false); }; fetchData(); }, [db]);

return ( <> <DataGridPro onCellKeyDown={handleCellKeyDown} disableRowSelectionOnClick={true} key={rows.length} columns={columns} sx={{ '& .MuiDataGrid-sortIcon': { color: 'blue', }, '& .MuiDataGrid-row': { backgroundColor: '#FFFF', margin: '0.5%', border: '1px solid #E8EDF2', borderRadius: '8px', width: '100%', }, }} rows={rows} hideFooterRowCount={true} loading={loading} hideFooterPagination sortingMode="server" onSortModelChange={handleSortModelChange} hideFooterSelectedRowCount rowCount={loading ? 0 : rows.length} onRowsScrollEnd={() => handleLoadMore(queryOptions.sortModel)} slots={{ loadingOverlay: LinearProgress, columnSortedDescendingIcon: ArrowDropUpIcon, columnSortedAscendingIcon: ArrowDropDownIcon, }} /> <AddRowButton isIconPresentOngrid={true} iconReceived={addIcon} buttonText="Add row" onClick={handleAddRow} /> </> ); };

export default MuiDataGrid;

Context 🔦

import * as React from 'react'; import { DataGridPro, GridSortModel } from '@mui/x-data-grid-pro'; import { where } from 'firebase/firestore'; import LinearProgress from '@mui/material/LinearProgress'; import { db } from '@/app/firebase/config'; import { useContext, useEffect, useState } from 'react'; // Remove this line import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp'; import AddRowButton from '../../global/IconAndButton'; import AddIcon from '@mui/icons-material/Add';

import { collection, getDocs, limit, orderBy, query, startAfter, } from 'firebase/firestore'; import { AvatarContext } from '@/app/contexts/avatar.context'; import GridChipsWithDropDown from '../../global/GridChips';

const addIcon = ( <AddIcon sx={{ color: '#6626E9', font: 'normal normal normal 16px/24px Font Awesome 6 Pro', }} /> );

const MuiDataGrid = () => { const handleAddRow = () => { const newRow = { id: rows.length + 1, prompt: '', completion: '', label: [], }; setRows([...rows, newRow]); }; const [loading, setLoading] = useState(true); const [rows, setRows] = useState([]); const [lastDoc, setLastDoc] = useState(null); const { avatar } = useContext(AvatarContext); const [queryOptions, setQueryOptions] = React.useState({ sortModel: [], }); const columns = [ { field: 'prompt', headerName: 'PROMPT', flex: 1, editable: true, headerClassName: 'cva-ai-grid-theme', }, { field: 'completion', headerName: 'COMPLETION', flex: 1, editable: true, headerClassName: 'cva-ai-grid-theme', }, { field: 'labels', headerName: 'LABELS', flex: 1, sortable: false, headerClassName: 'cva-ai-grid-theme', renderCell: (params: any) => ( <GridChipsWithDropDown handleKeyDownEvent={(event) => handleCellKeyDown(params, event)} labels={params.row.label} /> ), }, ];

const createQueryBasedOnModels = (sortModel: GridSortModel) => { let q;

if (sortModel && sortModel.length && sortModel[0].field) {
  q = query(
    collection(db, 'personalities', avatar.id, 'prompts_completion'),
    where(sortModel[0].field, '>=', ''),
    orderBy(sortModel[0].field, sortModel[0].sort),
    limit(25)
  );
} else {
  q = query(
    collection(db, 'personalities', avatar.id, 'prompts_completion'),
    orderBy('index'),
    limit(25)
  );
}
if (lastDoc) {
  q = query(q, startAfter(lastDoc));
}
return q;

};

const handleSortModelChange = async (sortModel: GridSortModel) => { setQueryOptions((prevState) => { return { sortModel: [...sortModel], }; }); if (queryOptions.sortModel && queryOptions.sortModel.length) { setLoading(true); const q = createQueryBasedOnModels(sortModel); const querySnapshot = await getDocs(q); const newRows = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setRows([...newRows]); setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]); setLoading(false); } };

const handleCellKeyDown = (params, event) => { if (event.key === ' ') { event.stopPropagation(); } };

const handleLoadMore = async (sortModel: GridSortModel) => { if (lastDoc) { setLoading(true); const q = createQueryBasedOnModels(sortModel); const querySnapshot = await getDocs(q);

  const newRows = querySnapshot.docs.map((doc) => ({
    id: doc.id,
    ...doc.data(),
  }));
  setRows([...rows, ...newRows]);
  setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]);
  setLoading(false);
} else {
  return;
}

};

useEffect(() => { const fetchData = async () => { setLoading(true); const q = query( collection(db, 'personalities', avatar.id, 'prompts_completion'), // where('field', '==', 'value'), orderBy('index'), limit(25) ); const querySnapshot = await getDocs(q); const newRows = querySnapshot.docs.map((doc) => ({ id: doc.id, ...doc.data(), })); setRows(newRows); setLastDoc(querySnapshot.docs[querySnapshot.docs.length - 1]); setLoading(false); }; fetchData(); }, [db]);

return ( <> <DataGridPro onCellKeyDown={handleCellKeyDown} disableRowSelectionOnClick={true} key={rows.length} columns={columns} sx={{ '& .MuiDataGrid-sortIcon': { color: 'blue', }, '& .MuiDataGrid-row': { backgroundColor: '#FFFF', margin: '0.5%', border: '1px solid #E8EDF2', borderRadius: '8px', width: '100%', }, }} rows={rows} hideFooterRowCount={true} loading={loading} hideFooterPagination sortingMode="server" onSortModelChange={handleSortModelChange} hideFooterSelectedRowCount rowCount={loading ? 0 : rows.length} onRowsScrollEnd={() => handleLoadMore(queryOptions.sortModel)} slots={{ loadingOverlay: LinearProgress, columnSortedDescendingIcon: ArrowDropUpIcon, columnSortedAscendingIcon: ArrowDropDownIcon, }} /> <AddRowButton isIconPresentOngrid={true} iconReceived={addIcon} buttonText="Add row" onClick={handleAddRow} /> </> ); };

export default MuiDataGrid;

Your environment 🌎

npx @mui/envinfo ``` Don't forget to mention which browser you used. Output from `npx @mui/envinfo` goes here. ```
adityapatil9191 commented 1 year ago

I am using chrome browser.

m4theushw commented 1 year ago

Please provide a minimal reproduction test case with the latest version. This would help a lot 👷 . A live example would be perfect. This codesandbox.io template may be a good starting point. Thank you!

adityapatil9191 commented 1 year ago

Hello Matheus,

Please find the url https://codesandbox.io/s/data-grid-community-forked-nqg9v8 for your reference. I have added necessary code for your reference.

Thanks, Aditya

On Fri, May 19, 2023 at 5:50 PM Matheus Wichman @.***> wrote:

Please provide a minimal reproduction test case with the latest version. This would help a lot 👷 . A live example would be perfect. This codesandbox.io template https://codesandbox.io/s/datagrid-v5-quick-start-54iz1 may be a good starting point. Thank you!

— Reply to this email directly, view it on GitHub https://github.com/mui/mui-x/issues/9042#issuecomment-1554489521, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIA7R5VGP4JKT2YXH6DSFZLXG5QPNANCNFSM6AAAAAAYHKP3TQ . You are receiving this because you authored the thread.Message ID: @.***>

adityapatil9191 commented 1 year ago

I am fetching data from Firebase.

On Fri, May 19, 2023 at 6:16 PM aditya patil @.***> wrote:

Hello Matheus,

Please find the url https://codesandbox.io/s/data-grid-community-forked-nqg9v8 for your reference. I have added necessary code for your reference.

Thanks, Aditya

On Fri, May 19, 2023 at 5:50 PM Matheus Wichman @.***> wrote:

Please provide a minimal reproduction test case with the latest version. This would help a lot 👷 . A live example would be perfect. This codesandbox.io template https://codesandbox.io/s/datagrid-v5-quick-start-54iz1 may be a good starting point. Thank you!

— Reply to this email directly, view it on GitHub https://github.com/mui/mui-x/issues/9042#issuecomment-1554489521, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIA7R5VGP4JKT2YXH6DSFZLXG5QPNANCNFSM6AAAAAAYHKP3TQ . You are receiving this because you authored the thread.Message ID: @.***>

adityapatil9191 commented 1 year ago

setRows([...rows, ...newRows]); this is adding new rows to my existing rows

adityapatil9191 commented 1 year ago

This is my package.json looks like

{ "name": "sample-web-app", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "lint": "next lint" }, "dependencies": { "@emotion/react": "^11.11.0", "@emotion/styled": "^11.11.0", "@fontsource/roboto": "^4.5.8", "@fortawesome/fontawesome-svg-core": "^6.4.0", "@fortawesome/free-brands-svg-icons": "^6.4.0", "@fortawesome/free-regular-svg-icons": "^6.4.0", "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", "@google-cloud/storage": "^6.9.5", "@mui/icons-material": "^5.11.11", "@mui/material": "^5.13.0", "@mui/x-data-grid-generator": "^6.3.1", "@mui/x-data-grid-pro": "^6.3.1", "@stripe/react-stripe-js": "^2.1.0", "@stripe/stripe-js": "^1.52.0", "@types/node": "18.15.3", "@types/react": "18.0.28", "@types/react-dom": "18.0.11", "@types/react-window": "^1.8.5", "encoding": "^0.1.13", "eslint": "8.36.0", "eslint-config-next": "^13.2.4", "firebase": "^9.17.1", "firebase-admin": "^11.5.0", "firebase-frameworks": "^0.7.0", "next": "^13.2.4", "papaparse": "^5.4.1", "react": "^18.2.0", "react-csv-reader": "^4.0.0", "react-dom": "^18.2.0", "react-hook-form": "^7.43.9", "react-infinite-scroll-component": "^6.1.0", "react-papaparse": "^4.1.0", "react-scroll": "^1.8.9", "react-virtualized": "^9.22.5", "react-window": "^1.8.9", "typescript": "4.9.5" }, "devDependencies": { "@types/papaparse": "^5.3.7", "@types/react-scroll": "^1.8.7", "@types/react-virtualized": "^9.21.21", "autoprefixer": "^10.4.14", "postcss": "^8.4.21", "tailwindcss": "^3.2.7" } }

adityapatil9191 commented 1 year ago

So the problem is whenever new data is fetched from the firebase. The scroll position is reset to the top of the MUI gird.

adityapatil9191 commented 1 year ago

Another issue :- How do I get the last updated values from a cell of MUI data grid?

adityapatil9191 commented 1 year ago

Hello Team,

We are answerable to our clients and this issue is kind of blocking us from the release. Your early response/help is highly appreciated.

Thank you, Aditya

On Fri, May 19, 2023 at 5:50 PM Matheus Wichman @.***> wrote:

Please provide a minimal reproduction test case with the latest version. This would help a lot 👷 . A live example would be perfect. This codesandbox.io template https://codesandbox.io/s/datagrid-v5-quick-start-54iz1 may be a good starting point. Thank you!

— Reply to this email directly, view it on GitHub https://github.com/mui/mui-x/issues/9042#issuecomment-1554489521, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIA7R5VGP4JKT2YXH6DSFZLXG5QPNANCNFSM6AAAAAAYHKP3TQ . You are receiving this because you authored the thread.Message ID: @.***>

m4theushw commented 1 year ago

Can you provide a example without Firebase? You can mock the response from the backend like we do in the example from https://mui.com/x/react-data-grid/row-updates/#infinite-loading

adityapatil9191 commented 1 year ago

Issue is not replicable with mock backend response and the scroll is occuring correctly. Can you please try it with some firebase setup from your end ?

adityapatil9191 commented 1 year ago

With similar queries maybe?

adityapatil9191 commented 1 year ago

https://www.loom.com/share/87d5edb485954206a5c07deb57d0b3da

PLeae find the above loom link for your reference.

adityapatil9191 commented 1 year ago

Hello,

Please find the loom link below for your reference. https://www.loom.com/share/87d5edb485954206a5c07deb57d0b3da

On Fri, May 19, 2023 at 11:44 PM Matheus Wichman @.***> wrote:

Can you provide a example without Firebase? You can mock the response from the backend like we do in the example from https://mui.com/x/react-data-grid/row-updates/#infinite-loading

— Reply to this email directly, view it on GitHub https://github.com/mui/mui-x/issues/9042#issuecomment-1555054506, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIA7R5VKUZUZWV6JBLW7XC3XG62CBANCNFSM6AAAAAAYHKP3TQ . You are receiving this because you authored the thread.Message ID: @.***>

m4theushw commented 1 year ago

Could you share a GitHub repo with the reproduction? We can't debug this issue without a fully working example.

adityapatil9191 commented 1 year ago

I am not sure if can share the repo URL . I can definitely share some code snippets. Maybe an entire file.

m4theushw commented 1 year ago

You can create a fake application connected to a fake Firebase instance and share with us via GitHub.

adityapatil9191 commented 1 year ago

https://codesandbox.io/s/data-grid-community-forked-nq4pnj Hey Finally I am able to replicate the issue. It is a working model

adityapatil9191 commented 1 year ago

Hey Matheus,

https://codesandbox.io/s/data-grid-community-forked-nq4pnj

PLease find the issue above. its a fake application but the issue is replicable. Your urgent help is highly appreciated.

Thanks & Regards, Aditya

On Sat, May 20, 2023 at 2:13 AM Matheus Wichman @.***> wrote:

You can create a fake application connected to a fake Firebase instance and share with us via GitHub.

— Reply to this email directly, view it on GitHub https://github.com/mui/mui-x/issues/9042#issuecomment-1555225208, or unsubscribe https://github.com/notifications/unsubscribe-auth/AIA7R5TCYZCWET2PI6JZ3DDXG7LP3ANCNFSM6AAAAAAYHKP3TQ . You are receiving this because you authored the thread.Message ID: @.***>

adityapatil9191 commented 1 year ago

I suspect that there isn't any issue with the code as such. But incase if you need more help please let me know.

adityapatil9191 commented 1 year ago

Please confirm if the work has already started on this.

cherniavskii commented 1 year ago

This is expected behavior because you're changing the key prop on the data grid component:

<DataGridPro
  key={rows.length}
/>

The grid state is reset each time the key prop changes, so I don't think you should pass rows.length as a key. See https://react.dev/reference/react/useState#resetting-state-with-a-key for more information.

adityapatil9191 commented 1 year ago

Thank you so much!!!!!!!