Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix FormTokenField rendering #14819

Merged
merged 3 commits into from
Jul 31, 2019
Merged

Conversation

tfrommen
Copy link
Member

@tfrommen tfrommen commented Apr 4, 2019

Description

The FormTokenField component takes both the list of values to select from and the list of currently selected values as props.

However, updating and rendering the component only relies on user interaction (i.e., changing what is in the input). Any changes to either the selectable values or the selected values is ignored.

This results in bad UX in case the values are fetched (asynchronously). The behavior is that you would enter something, WordPress goes and fetches the results, passes them to the component, ... and nothing. Rendering will only happen on the next change in the input.

(By the way, this is the exact situation with any non-hierarchical taxonomy input, for example, Tags.)

This PR fixes that logic, and updates the selection if either of the selectable or selected values changes.

How has this been tested?

Steps to reproduce:

A real-life use case is the Tags panel input. Assuming you have a post tag "test". Typing te into the input will kick off an according API call, and you get your response back. But nothing happens. At least not visually. The component has received the updated prop, but only on a third keystroke, say typing "s" to have "tes", you see the result.

Screenshots

Types of changes

Fix logic in rendering/reselection.

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.

@gziolo gziolo added [Type] Enhancement A suggestion for improvement. [Feature] UI Components Impacts or related to the UI component system Good First Review A PR that's suitable for someone looking to contribute for the first time by reviewing code labels Apr 5, 2019
@tfrommen
Copy link
Member Author

tfrommen commented Apr 10, 2019

@gziolo I just added a small unit test ensuring that changing the suggestions prop actually updates/re-renders the suggestions.

With the code in this branch, the new test passes. However, if one were to change this:

const suggestionsDidUpdate = suggestions !== prevProps.suggestions;
if ( suggestionsDidUpdate || value !== prevProps.value ) {
this.updateSuggestions( suggestionsDidUpdate );
}

to this:

    const suggestionsDidUpdate = suggestions !== prevProps.suggestions; 
    // if ( suggestionsDidUpdate || value !== prevProps.value ) { 
    if ( value !== prevProps.value ) { 
        this.updateSuggestions( suggestionsDidUpdate ); 
    }

the new test fails, which is exactly what we want here.

@aduth
Copy link
Member

aduth commented Apr 29, 2019

Can you include some testing instructions here for a reviewer to know how to verify the intended changes?

@tfrommen
Copy link
Member Author

tfrommen commented Apr 29, 2019

@aduth in essence, this PR updates the rendered suggestions in case a depended-upon prop, suggestions, changes. So, manually changing the suggestions prop will not re-render the suggestions list, but it should.

A real-life use case is the Tags panel input. Assuming you have a post tag "test". Typing te into the input will kick off an according API call, and you get your response back. But nothing happens. At least not visually. The component has received the updated prop, but only on a third keystroke, say typing "s" to have "tes", you see the result.

Not sure this helps...?

@aduth
Copy link
Member

aduth commented Apr 29, 2019

A real-life use case is the Tags panel input. Assuming you have a post tag "test". Typing te into the input will kick off an according API call, and you get your response back. But nothing happens. At least not visually. The component has received the updated prop, but only on a third keystroke, say typing "s" to have "tes", you see the result.

That helps clarify, thanks. Including this sort of steps in the initial comment a reviewer knows what to be looking for (like a bug report's "Steps to Reproduce").

@tfrommen tfrommen added the [Package] Components /packages/components label May 19, 2019
@tfrommen
Copy link
Member Author

@aduth @gziolo this is waiting for quite some time now, so I was wondering if there's anything (for me) to do here? 🙂

I hope to have explained the issue, the fix and steps to test/reproduce, and there are some unit tests.
Should I expand on any of these, or is there something else missing or not quite right yet?

Thanks!

// Make sure to focus the input when the isActive state is true.
if ( this.state.isActive && ! this.input.hasFocus() ) {
this.input.focus();
}

const { suggestions, value } = this.props;
const suggestionsDidUpdate = suggestions !== prevProps.suggestions;
Copy link
Member

Choose a reason for hiding this comment

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

This probably updates more than you're expecting it to, at least based on the shape of suggestions being an array, and the array being generated as a new value on each render.

Essentially, it comes down to:

const a = [ 'foo' ];
const b = [ 'foo' ];
console.log( a === b );
// false

Related: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html

At least in how this is used by FlatTermSelector, the array is a result of Array#map, so would be a new value on each render:

const termNames = availableTerms.map( ( term ) => term.name );

The map() method creates a new array with the results of calling a provided function on every element in the calling array.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map

Options:

  • Accept the fact that it renders more than we expect it to, but that it might benefit from some reuse if the references stay the same
  • Abandon any attempt to avoid updating and always update suggestions
  • Consider instead equality of members for sameness (@wordpress/is-shallow-equal). There is a small performance overhead to this, if it's worth considering the costliness vs. updateSuggestions (or whether updateSuggestions must only be called when in-fact it changes)

Copy link
Member Author

Choose a reason for hiding this comment

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

Hey @aduth - thanks for the feedback, and sorry for the super late reply. I missed this comment (and only read the review one further down). 🤦‍♂

You are absolutely right, and I now implemented isShallowEqual to compare suggestions. 👍

Is this PR all good now? (Build and tests are still pasing.) 🙂

@aduth
Copy link
Member

aduth commented May 21, 2019

Apologies for the delay in revisiting this. There's quite a backlog for reviews. In case you find yourself in a similar situation, please feel welcomed to review other pull requests while waiting to help contribute to a lower review turnaround.

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

Thanks for the patience, and for the fix! This looks good to me 👍

Aside: I found it easiest to verify the fix using Chrome's built-in network throttling options.

selectedSuggestionScroll: false,
isExpanded: false,
} );
this.setState( { incompleteTokenValue: tokenValue }, this.updateSuggestions );
Copy link
Member

Choose a reason for hiding this comment

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

Technically we could avoid this.updateSuggestions as the callback here and consolidate instead to componentDidUpdate (which can also detect the state change).

@aduth aduth merged commit f380cc4 into WordPress:master Jul 31, 2019
@tfrommen tfrommen deleted the fix-form-token-field branch August 5, 2019 07:55
@youknowriad youknowriad added this to the Gutenberg 6.3 milestone Aug 9, 2019
gziolo pushed a commit that referenced this pull request Aug 29, 2019
* Fix rendering/reselection upon prop changes

* Add unit test

* Use isShallowEqual to compare suggestions
gziolo pushed a commit that referenced this pull request Aug 29, 2019
* Fix rendering/reselection upon prop changes

* Add unit test

* Use isShallowEqual to compare suggestions
@sgrund14
Copy link

sgrund14 commented Apr 8, 2020

for whatever reason I'm not getting this behavior when importing from @wordpress/components @9.3.0. the asynchronous fetch happens, updates the suggestions being passed into FormTokenField, but then... the suggestion list doesn't expand until I type in the input again!

but the really strange thing is, if I copy over the source code from node_modules into my own component, this new behavior is working. any idea what might be happening @aduth @tfrommen?

@aduth
Copy link
Member

aduth commented Apr 13, 2020

Hi @sgrund14 , it's been some time since I looked at this pull request, so I can't exactly recall what the problem might be based on what you describe. Regarding the difference from node_modules, one thing I could think of is if your first scenario is referring to an older version of the package, specifically one shipped with WordPress (and perhaps an older version of WordPress). These scripts can lag behind the versions which are published to NPM, so that could potentially explain a difference you're seeing. It depends if your code is referencing the globals (wp.components), which is also configured in most of the default developer tooling offered through @wordpress/scripts (in case you're using it).

I'd suggest if you suspect there to be a bug in the component, to create a new issue, since it'll be easier to follow-up on than to continue the discussion in this old pull request.

@sgrund14
Copy link

I'm not referencing the globals, but I'll open up a new issue. Thanks @aduth :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] UI Components Impacts or related to the UI component system Good First Review A PR that's suitable for someone looking to contribute for the first time by reviewing code [Package] Components /packages/components [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants