Skip to content

Search ajax doesn't cancel for superseded searches #222

@cjbarth

Description

@cjbarth

When searching on BigCommerce, the search function can be called multiple times, but subsequent calls will not cause previous in-flight calls to be canceled. This creates a problem because any UI component, even the default theme, will call this API and will get the responses out-of-order. Thus, the rendered quick-list is not guaranteed to show the results that match what is in the search box.

Here is a real-life example:

Image

You can see the results of the search for "1" returned after nearly 4 seconds. I can easily type the entire number in 4 seconds. In fact, the last search is .6 seconds. So, as long as I can type the number in <3.4 seconds, I'll get my result for the full number, and it will render, before the result for the first number returns. So, in that case, the search results list that shows will always show the results for the first search for "1" before the results for "100000"

This is an easy fix. You could use an AbortController.

For example, that search.js that I linked to above could be replaced this this:

import Hooks from '../hooks';
import Base from './base';

export default class extends Base {
    /**
     * @Constructor
     */
    constructor(version) {
        // call parent
        super(version);

        // set up class variables
        this.endpoint = '/search.php?search_query=';
        this._searchController = null;
    }

    /**
     * Get search results
     * @param {String} query
     * @param {Object} params
     * @param {Function} callback
     */
    search(query, params, callback) {
        const url = this.endpoint + encodeURIComponent(query);
        let paramsArg = params;
        let callbackArg = callback;

        if (typeof paramsArg === 'function') {
            callbackArg = paramsArg;
            paramsArg = {};
        }

        if (this._searchController) {
          this._searchController.abort();
        }

        this._searchController = new AbortController();
        paramsArg.signal = controller.signal;

        Hooks.emit('search-quick-remote', query);
        this.makeRequest(url, 'GET', paramsArg, false, callbackArg);
    }
}

Of course, then you'd have to modify /lib.request.js to be like this:

    const config = {
        method: options.method,
        headers,
        credentials: 'include',
        signal: options.signal,
    };

This is a real-world problem affecting clients. For example, see BigCommerce issue STRF-13076. If you'd like a PR for this, please let me know and I'll put one up for you.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions