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

Conversation

@tfrommen
Copy link
Member

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.
@tfrommen

This comment has been minimized.

Copy link
Member Author

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

This comment has been minimized.

Copy link
Member

commented Apr 29, 2019

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

@tfrommen

This comment has been minimized.

Copy link
Member Author

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Member Author

commented May 19, 2019

@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;

This comment has been minimized.

Copy link
@aduth

aduth May 21, 2019

Member

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)

This comment has been minimized.

Copy link
@tfrommen

tfrommen Jul 18, 2019

Author Member

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

This comment has been minimized.

Copy link
Member

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.

@aduth
aduth approved these changes Jul 31, 2019
Copy link
Member

left a comment

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 );

This comment has been minimized.

Copy link
@aduth

aduth Jul 31, 2019

Member

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

1 check passed

Travis CI - Pull Request Build Passed
Details

@tfrommen tfrommen deleted the tfrommen:fix-form-token-field branch Aug 5, 2019

@youknowriad youknowriad added this to the Gutenberg 6.3 milestone Aug 9, 2019

gziolo added a commit that referenced this pull request Aug 29, 2019
Fix FormTokenField rendering (#14819)
* Fix rendering/reselection upon prop changes

* Add unit test

* Use isShallowEqual to compare suggestions
gziolo added a commit that referenced this pull request Aug 29, 2019
Fix FormTokenField rendering (#14819)
* Fix rendering/reselection upon prop changes

* Add unit test

* Use isShallowEqual to compare suggestions
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.