mbrn / material-table

Datatable for React based on material-ui's table with additional features
https://material-table.com
MIT License
3.49k stars 1.03k forks source link

Data is updated, Material-table doesn't show updates until refresh #2526

Closed Bryceacampbell closed 3 years ago

Bryceacampbell commented 3 years ago

Hello, I am running into an issue and could use some help. I am using apollo client to fetch some lead data and pass it into the material table (this works great). I have a side-drawer that opens on row click with the dynamic row data and I have a form inside the side-drawer that allows a user to update the row data. However, material-table doesn't render the updates until I refresh the page.

Leads.js is the parent component that fetches the leads, and sets the leads into state using react hooks.

Leads.js

const Leads = () => {

    const [userLeadsLoaded, setUserLeadsLoaded] = React.useState(false);
    const [userLeads, setUserLeads] = React.useState([]);
    const { isAuthenticated, user, loading } = useAuth()

    const params = { id: isAuthenticated ? user.id : null };

    const {
        loading: apolloLoading,
        error: apolloError,
        data: apolloData,
    } = useQuery(GQL_QUERY_ALL_LEADS, {
        variables: params,
    });

    useEffect(() => {
        if (apolloData) {
            if (!userLeadsLoaded) {
                const { leads } = apolloData;
                const editable = leads.map(o => ({ ...o }));
                setUserLeads(editable);
                setUserLeadsLoaded(true);
            };
        }
    }, [apolloData])

    if (apolloError) {
        console.log(apolloError)
        //TODO: Do something with the error, ie default user?
        return (
            <div>
                <div>Oh no, there was a problem. Try refreshing the app.</div>
                <pre>{apolloError.message}</pre>
            </div>
        );
    };
    return (
        <>
                {apolloLoading
                    ? (<CircularProgress variant="indeterminate" />)
                    : (<LeadsTable leads={userLeads} setLeads={setUserLeads} />)
                }
        </>
    )
}

export default Leads

LeadsTable.js is where I take in the leads as props and pass it into material-table. There is an onRowClick event that opens a side drawer with dynamic lead details.

LeadsTable.js

const LeadsTable = ({ leads, setLeads }) => {
  const classes = useStyles();
  const { user } = useAuth();
  const [isLeadDrawerOpen, setIsLeadDrawerOpen] = React.useState(false);
  const [selectedRow, setSelectedRow] = React.useState({});
  const columns = React.useMemo(() => columnDetails);

  const handleClose = () => {
    setIsLeadDrawerOpen(!isLeadDrawerOpen);
  }

  return (
    <>
      <MaterialTable
        title='Leads'
        columns={columns}
        data={leads}
        icons={tableIcons}
        options={{
          exportButton: false,
          hover: true,
          pageSize: 10,
          pageSizeOptions: [10, 20, 30, 50, 100],
        }}
        onRowClick={(event, row) => {
          console.log('Selected Row:', row)
          setSelectedRow(row);
          setIsLeadDrawerOpen(true);
        }}
        style={{
          padding: 20,
        }}
      />
      <Drawer
        variant="temporary"
        open={isLeadDrawerOpen}
        anchor="right"
        onClose={handleClose}
        className={classes.drawer}
      >

        <LeadDrawer onCancel={handleClose} lead={selectedRow} setLeads={setLeads} leads={leads} />

      </Drawer>
    </>
  );
};

export default LeadsTable;

LeadDrawer.js has navigation tabs and the update form lives inside the overview tab here. You can see I am passing in the setLeads and leads props

Overview.js

const Overview = (props) => {
    const classes = useStyles();
    const { lead, leads, setLeads } = props;
    const { user } = useAuth();

    return (
        <>
               //this is the form to update a leads information
               <AddAdditionalInfo setSelectedRow={setSelectedRow} leads={leads} setLeads={setLeads} lead={lead} />
        </>
    )
};

export default Overview;

and finally inside the AddAdditionalInfo form I have a handle submit function that runs an apollo mutation to update the lead in the DB and setLeads to the new updated leads

AddAdditionalInfo.js

const handleSubmit = async (event) => {
        event.preventDefault();

        const updatedLead = {
            id: leadState.id,
            first_name: leadState.firstName,
            last_name: leadState.lastName,
            email_one: leadState.email,
            address_one: leadState.addressOne,
            address_two: leadState.addressTwo,
            city: leadState.city,
            state_abbr: leadState.state,
            zip: leadState.zipCode,
            phone_cell: leadState.phone,
            suffix: suffix,
            address_verified: true
        }
        const { data } = await updateLead({
            variables: updatedLead,
            refetchQueries: [{ query: GQL_QUERY_GET_USERS_LEADS, variables: { id: user.id } }]
        })
        const newLeads = updateIndexById(leads, data.updateLead)
        setLeads(state => newLeads)
        handleClose()
    };

Summary: I am updating the leads state by calling setLeads and material-table isn't rendering the changes unless I refresh the page. I am not assuming it is a problem with material table, I'm just looking for some help or maybe a second eye. I have been stuck on this little problem for far too long. Maybe I just need a different approach... Any ideas or help would be much appreciated.

tarwich commented 3 years ago

FWIW I'm having the same problem. In my situation I'm using mobx

const store = useLocalStore(() => ({
  data: [
    {id: 1, name: 'aaa'},
    {id: 2, name: 'bbb'}
  ]
}));

return useObserver(() => (
  <div>
    <button onClick={() => {
      store.data[0].name = 'zzz';
    }}>Test</button>
    <MaterialTable
      data={store.data}
      columns={[
        {title: 'Id', field: 'id'},
        {title: 'Name', field: 'name'},
      ]}
      />
  </div>
));

If I can figure it out I'll let you know

RoosterH commented 3 years ago

I am having the same issue, the new value indeed got passed to the table but table did not update it unless a manual click on the table. Here is the link of my issue: https://github.com/mbrn/material-table/issues/2693

RoosterH commented 3 years ago

I resolved my issue by updating the value inside onClick. Here is what I did:

const [data, setData] = useState(props.entryList);
.....
actions={[
    {
            icon: 'Charge',
            tooltip: 'Charge User',
        onClick: (event, rowData) => {
                 // setData here to force table update using new value
        setTimeout(() => {
            setData([rowData]);
        }, 1000);
    }
    }
]}

Hope this helps.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. You can reopen it if it required.

AmrViernes commented 2 years ago

if this still a stand issue for some just use refetch from useQuery() to update table data after an edit or create