Skip to content

Conversation

jcfilben
Copy link
Collaborator

@jcfilben jcfilben commented Jul 8, 2022

What does this PR do?

Prevents the debounce function from executing if the FormField is unmounted.

Where should the reviewer start?

What testing has been done on this PR?

Tested with the following story and console log statements in the debounce function

import React, { useState } from 'react';
import { Box, Form, FormField, TextInput, Button } from 'grommet';

export const TEMP = () => {
  const [showForm, setShowForm] = useState(true);

  return (
    <Box fill align="center" justify="center">
      <Box width="medium">
        {showForm && <MyForm setShowForm={setShowForm} />}
        <Button
          label="Show form"
          onClick={() => {
            setShowForm(true);
          }}
        />
      </Box>
    </Box>
  );
};

const MyForm = ({ setShowForm }) => (
  <Form
    validate="change"
    onChange={(val) => {
      console.log(`onChange ${val}`);
      setShowForm(false);
    }}
  >
    <FormField
      name="myField"
      validate={(val) => {
        console.log(`validate ${val}`);
      }}
    >
      <TextInput name="myField" placeholder="type here" />
    </FormField>
  </Form>
);

TEMP.storyName = 'TEMP';
TEMP.args = {
  full: true,
};

export default {
  title: 'Input/Form/TEMP',
};

How should this be manually tested?

Do Jest tests follow these best practices?

  • screen is used for querying.
  • The correct query is used. (Refer to this list of queries)
  • userEvent is used in place of fireEvent.
  • asFragment() is used for snapshot testing.

Any background context you want to provide?

What are the relevant issues?

Closes #6130
Closes #6129

Screenshots (if appropriate)

Do the grommet docs need to be updated?

It might be good to add the debounceDelay theme prop to the FormField doc. I will create a PR for this.

Should this PR be mentioned in the release notes?

Maybe

Is this change backwards compatible or is it a breaking change?

Backwards compatible

@jcfilben jcfilben requested a review from halocline July 8, 2022 17:12
Copy link
Collaborator

@halocline halocline left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested. Cancelation works as expected. Nice job.

// long depending on how fast people type, and 200ms
// would be an eye blink
},
500,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if this delay should be something the caller can configure? Especially since we don't know what kind of validation function might be called. There is a theme prop, but seems to have been abandoned...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree that this should be configurable. Since debounceDelay is not being used currently I think we could change it from '300' to '500' and then use it here

const debouncedFn = debounce(
() => {
contextOnChange(event);
// A half second (500ms) debounce can be a helpful
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this comment into base.js, since that is where the value is being set?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, done

// past it. 2 second (2000ms) might be too long depending
// on how fast people type, and 200ms would be an eye blink
}, 500);
const debouncedFn = debounce(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we remove the Fn suffix? It doesn't seem consistent with grommet's naming convention to include typing in variable names.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -134,12 +134,14 @@ const Input = ({ component, disabled, invalid, name, onChange, ...rest }) => {
);
};

const debounce = (func, wait) => {
const debounce = (func, wait, fieldRef) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of making debounce() know about the ref, what about changing it to be a hook that will clear the timer automatically when the component is unmounted?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, I updated to use a hook in the latest commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A suggestion to tighten it up a bit:

const useDebounce = () => {
  const [func, setFunc] = useState();

  useEffect(() => {
    const timer = setTimeout(() => func(), theme.global.debounceDelay);
    return () => clearTimeout(timer);
  }, [func]);
  
  return setFunc;
};

...
const debounce = useDebounce();
...
if (contextOnChange) debounce(() => contextOnChange(event)));

untested, but perhaps you get the idea?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this in latest commit

@jcfilben jcfilben requested a review from ericsoderberghp July 15, 2022 22:17
}, 500);
debouncedFn();
}
if (contextOnChange) setDebounced(() => contextOnChange);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seem to have lost the event argument to contextOnChange()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in latest commit

@jcfilben jcfilben requested a review from ericsoderberghp July 18, 2022 15:48
Copy link
Contributor

@ericsoderberghp ericsoderberghp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, one simple comment

@@ -194,6 +183,19 @@ const FormField = forwardRef(
const { formField: formFieldTheme } = theme;
const { border: themeBorder } = formFieldTheme;

const useDebounce = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this hook definition can be outside the component, above line 138.

@ericsoderberghp ericsoderberghp merged commit a6a561e into grommet:master Jul 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Form sets state after onChange debounce delay without ensuring component is still mounted FormField onChange validation delay is hardcoded to 500ms
3 participants