0

I have a table I need to generate based on 4 inputs. The inputs need to be filled out and then a button must be clicked for the table to generate. Also, the table should re-generate if the page is changed. So basically, I need the function to run only when the button is pressed or the pages change.

Because of pagination I have implemented useEffect/useCallback. The issue however is with the useCallback. Because the 4 inputs are passed to useCallback as a dependency, the table generates before I click the button and I don't want that to happen. I cannot remove the inputs as a dependency.

I could add another state, change the state after the button is pressed and add that state as a dependency in useEffect, so that each time I press the button, state is changed therefore useEffect is called -> table gets rerendered. This occurred to me but I am not sure I want to add another state to the mix as it seems wasteful.

The 4 inputs are passed through from the parent component as props.

How do I:

  1. Prevent the generateTable from exectuing before I click the button
  2. Execute generateTable after the button click

The code:

const Table: FC<Table> = ({ values }) => {
  const [page, setPage] = useState(1);

  const [getData, getDataResponse] = useLazyQuery<
    IQueryResponse,
    IQueryInput
  >(QUERY_NAME), {
    fetchPolicy: 'no-cache',
    onError: error => {},
  });

  const handleButtonOnClick = () => {
    generateTable();
  };

  const handlePageChange = (_event: unknown, value: number) => {
    setPage(value);
  };

  const generateTable = useCallback(() => {
    if (
      values?.input1 &&
      values?.input2 &&
      values?.input3 &&
      values?.input4 
    ) {
      getData({
        variables: {
          input1 : values.input1,
          input2 : values.input2,
          input3 : values.input3,
          input4 : values.input4,
          limit: rowsPerPage,
          offset: rowsPerPage * (page - 1),
        },
      });
    }

  }, [getData, page, values]);

  useEffect(() => {
    generateTable();
  }, [generateTable, page]);

  .
  .
  .
  
  return (
    <Button onClick={handleOnclick}>Generate table</Button>

    {getDataResponse.data ? (
      <Table> ... </Table>
      <Pagination page={page} onChange={handlePageChange} />
    ) : ''}
  )
muke01
  • 1
  • 1
  • You're running it the second you call it, you could possibly define the arrow function globally and call useCallback(newfunction(),[getData,page, values]); – BGPHiJACK May 05 '21 at 10:14

1 Answers1

0

I managed to solve the problem by simplifying the code and getting rid of the useEffect and useCallback hooks entirely and just passing the value that required the useEffect before.

const Table: FC<Table> = ({ values }) => {
  const [page, setPage] = useState(1);

  const [getData, getDataResponse] = useLazyQuery<
    IQueryResponse,
    IQueryInput
  >(QUERY_NAME), {
    fetchPolicy: 'no-cache',
    onError: error => {},
  });

  const handleButtonOnClick = () => {
    generateTable(page);
  };

  const handlePageChange = (_event: unknown, value: number) => {
    setPage(value);
    generateTable(value)
  };

  const generateTable = (page: number) => {
    if (
      values?.input1 &&
      values?.input2 &&
      values?.input3 &&
      values?.input4 
    ) {
      getData({
        variables: {
          input1 : values.input1,
          input2 : values.input2,
          input3 : values.input3,
          input4 : values.input4,
          limit: rowsPerPage,
          offset: rowsPerPage * (page - 1),
        },
      });
    }
  }

  .
  .
  .
  
  return (
    <Button onClick={handleOnclick}>Generate table</Button>

    {getDataResponse.data ? (
      <Table> ... </Table>
      <Pagination page={page} onChange={handlePageChange} />
    ) : ''}
  )
muke01
  • 1
  • 1