Material Table not updating table data after mutation

1.1k views Asked by At

When a user adds additional information, a mutation is made to the database adding the new info, then the local state is updated, adding the new information to the lead.

My mutation and state seem to get updated fine, the issue seems to be that the state of the Material Table component does not match its 'data' prop. I can see in the React Dev tools that the state was updated in the parent component and is being passes down, the table just seems to be using stale data until I manually refresh the page.

I will attach images of the React Devtools as well as some code snippets. Any help would be much appreciated.

Devtools Material Table data prop: Devtools Material Table State

Material Table Parent Component:

const Leads = () => {

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

    const [
        createLead,
        { data,
            // loading: mutationLoading,
            error: mutationError },
    ] = useMutation(GQL_MUTATION_CREATE_LEAD);

    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 (apolloLoading) {
        return (
            <>
                <CircularProgress variant="indeterminate" />
            </>
        );
    };

    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 (
        <>
            <Layout leadState={leadState} setLeads={setUserLeads} leads={userLeads} setLeadState={setLeadState} createLead={createLead}>
                {apolloLoading ? (<CircularProgress variant="indeterminate" />) : (<LeadsTable leads={userLeads} setLeads={setUserLeads} />)}
            </Layout>
        </>
    )
}

export default Leads

Handle Submit function for adding additional information:

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)
        console.log('New leads before setLeads: ', newLeads)
        setLeads(newLeads)
        // setSelectedRow(data.updateLead)
        handleClose()
    };

Material Table Component:

const columnDetails = [
  { title: 'First Name', field: 'first_name' },
  { title: 'Last Name', field: 'last_name' },
  { title: 'Phone Cell', field: 'phone_cell' },
  { title: 'Email', field: 'email_one' },
  { title: 'Stage', field: 'stage', lookup: { New: 'New', Working: 'Working', Converted: 'Converted' } },
  { title: 'Active', field: 'active', lookup: { Active: 'Active' } },

];

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);
  }
  console.log('All leads from leads table render: ', leads)
  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} setSelectedRow={setSelectedRow} leads={leads} />

      </Drawer>
    </>
  );
};

export default LeadsTable;
1

There are 1 answers

5
Ray On

Try creating an object that contains refetchQueries and awaitRefetchQueries: true. Pass that object to useMutation hook as a 2nd parameter. See example below:

const [
        createLead,
        { data,
            loading: mutationLoading,
            error: mutationError },
    ] = useMutation(GQL_MUTATION_CREATE_LEAD, {
  refetchQueries:  [{ query: GQL_QUERY_GET_USERS_LEADS, variables: { id: user.id } }],
  awaitRefetchQueries: true,
});

Manually updating cache. Example blow is adding a new todo. In your case you can find and update the record before writing the query.

const updateCache = (cache, {data}) => {
    
    // Fetch the todos from the cache
    const existingTodos = cache.readQuery({
      query: GET_MY_TODOS
    });
    // Add the new todo to the cache (or find and update an existing record here)
    const newTodo = data.insert_todos.returning[0];
    cache.writeQuery({
      query: GET_MY_TODOS,
      data: {todos: [newTodo, ...existingTodos.todos]}
    });
  };
  const [addTodo] = useMutation(ADD_TODO, {update: updateCache});