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

getting a lock #15

Open
vgoklani opened this issue Jan 12, 2016 · 15 comments
Open

getting a lock #15

vgoklani opened this issue Jan 12, 2016 · 15 comments

Comments

@vgoklani
Copy link

Hi,
I'm using react-infinite to render a column of data, and I use react-debounce-input for filtering the data. This works well most of the time. However, when a new piece of data comes through the pipeline, it gets automatically appended to the top of the list (via setState). Unfortunately what happens is that if the user is typing in a query while a piece of data is coming in, the search box gets scrambled (setState from the data column confuses the search query). Basically I need to add a LOCK such that if the user is in the middle of entering a query, the data column should not update. The problem is that I don't know how to get the lock from react-debounce-input? Any ideas?

I can control the setState from shouldComponentUpdate for react-infinite, but I'm not sure how to intercept the search box lock.

@nkbt
Copy link
Owner

nkbt commented Jan 12, 2016

While updating you can:

  1. Replace ReactDebounceInput with just a normal input, React will do the dirty job of removing anything around it and will keep the input, so user should not lose his input state
  2. Set debounceTimeout to -1 so it will not fire up updates. When your stuff is updated - revert it back to whatever value you had there previously

Those are two solutions I can quickly come up with. Let me know if it works out for you

Cheers,
Nik

@mvdwaeter
Copy link

I think I have the same problem. I use DebounceInput as a Searchfilter on a queryset.
E.g. the api URL = /api/2.0/nodes?q=searchword
Based on the json repsonse of that API it will display a table of results.
Because I don't want to do an API request on every key down event in the searchbox, I use DebounceInput.

However, sometimes the value of the input is not correct.
It's easiest to replicate by placing 1 letter in the DebounceInput, hitting backspace and quickly typing a new letter. This will result in no letters at all in the box.

<DebounceInput
    type="search"
    minLength={2}
    debounceTimeout={300}
    value={this.props.q}  // from state from a Store
    onChange={this.props.onChangeHandler}
    id="id_q"
>

Now this can be "solved" by removing the value attribute from the DebounceInput.
However, if I do that, then I'm unable to clear the value of DebounceInput with a onClick event of another button. I tried with jquery: $('#id_q').val('');

I'm thinking, because DebounceInput is a controlled component (https://facebook.github.io/react/docs/forms.html).

Is this even possible?

@nkbt
Copy link
Owner

nkbt commented Feb 9, 2016

placing 1 letter in the DebounceInput, hitting backspace and quickly typing a new letter. This will result in no letters at all in the box.

That sounds like a bug (though a different one to the topic)

I tried with jquery: $('#id_q').val('');

Do not modify DOM with any other tools other then React if you don't want to have completely unpredictable effects

DebounceInput is a controlled component

It can be both =) If you don't specify value it won't be controllable. Try to use defaultValue instead (usual one for any uncontrollable input in React)

Your use-case seems like an actually the main use-case for DebounceInput =). And the way we use it on our projects. So I am quite surprised it does not work as expected.

Would be great to have a Codepen example to play with, so we can find what could be the issue

@mvdwaeter
Copy link

Thank you for the quick reaction.
About the 1 letter in the box "bug"; this was overcome by placing minLength=0
You can reproduce it here:
http://nkbt.github.io/react-debounce-input/example/
and then slide Min Length to 2 or higher
type something in the first text field. Select all text in the first text field, and replace it with 1 character. The value is now blank/undefined. I'm not sure if this is a bug or a feature.

maybe it's because I use the DebounceInput like so:

----- SearchBox.jsx -------

import React from 'react';
import DebounceInput from 'react-debounce-input';

export default class SearchBox extends React.Component {
    render() {
      return (<DebounceInput
        type="search"
        minLength={0}
        debounceTimeout={300}
        defaultValue={this.props.q} // When I use defaultValue: No problem with scrabling my input, but now impossible to reset
        value={this.props.q} // When I use value: Problem with scrabling, but now possible to reset
        tabIndex={this.props.tabIndex ? this.props.tabIndex:"1"}
        placeholder={this.props.placeholder ? this.props.placeholder:"Search"}
        onChange={this.props.onChangeHandler}
        autoComplete={this.props.autoComplete ? this.props.autoComplete:"off"}
        name={this.props.name ? this.props.name : "q"}
        className={this.props.className ? this.props.className:"form-control"}
        id={"id_" + this.props.name ? this.props.name : "q"}/>);
    }
}

I can imagine there is a better way to extend DebounceInput.

@nkbt
Copy link
Owner

nkbt commented Feb 9, 2016 via email

@nkbt
Copy link
Owner

nkbt commented Feb 9, 2016

Also, if you are doing wrapping, then look at https://github.com/nkbt/react-text-filter (it is basically wrapped DebounceInput)

I remember having some problems with it similar to what you are trying to explain. See examples there and the code of TextInput how it keeps behaviour exactly the same to what you want.

@nkbt
Copy link
Owner

nkbt commented Feb 9, 2016

Double-checked the code of Filter and there seems like no issues wrapping Debounce. Everything is simple. I had problems initially but then fixed DebounceInput to allow easy extending.

So just look at it, maybe there is some tiny little thing that I do not notice in your code that makes things awkward...

@mvdwaeter
Copy link

ahhh I think I know it.
It's because the searchbox component I use is INSIDE the resultset.

So, I have a

component.
This has a component
In this TableFooter, there are searchboxes for each column.
Entering text in one searchbox alters the resultset on Table, which probably also triggers a new draw on the TableFooter and hence the searchbox, overwriting the text the user was typing with the search word.

For me to get it to work, is to move the search filters outside of the table which holds the result. I'm going to try this now.

@nkbt
Copy link
Owner

nkbt commented Feb 10, 2016

Oh cool! Let me know how it goes

@mvdwaeter
Copy link

Separating the components didn't seem to matter in my code, because (I suspect) they still use the same store (flux), and on updating of the store all components were updated.
E.g. the search string is changed in the store, then after an ajax request is done to get the new resultset, after that is done, it will update the store's state again.

So I think I have to read a bit more into flux and probably create more Stores or don't store the search value into the store but in the component itself.

@twmills
Copy link

twmills commented Feb 25, 2016

I am seeing a problem in a filter text box and typing relatively quickly. My on change function updates my redux store with the new value, but if I type a bit more quickly, the new characters tend to disappear.

Tellingly, it improves the lower I set the debounceTimeout value. When I set it to 1, the glitch disappears entirely.

Have you seen anything like that, or have any advice?

@nkbt
Copy link
Owner

nkbt commented Feb 29, 2016

@twmills this is how it works (or the reason it does not work as you expect it to work) in your case:

  1. type smth - abc in the input
  2. onChange invoked after debounce timeout - abc event.target.value
  3. store it in redux store - abc value in store
  4. meanwhile you keep typing - abcdef in the input
  5. store change subscriber reacts and sets value from redux store - back to abc
  6. ced are "lost"

It is completely expected behaviour when you have delayed input. If you want to keep it in redux store and have "controllable" input with instant updates, then why do you need debounced-input in the first place? What is the desired behaviour for you? I can most likely help you to design it

PS: if you want to set value of input when user opens the page from the last input - use defaultValue, not value

@huan086
Copy link

huan086 commented Apr 4, 2016

I have a similar issue in different use case. My use case is DebouncedInput onChange will trigger AJAX to search data. The data received includes the search text. There are also other components on the page that would trigger the same AJAX call, but with different search text. The DebouncedInput has to reflect the search text when it receives the data.

The AJAX might finish when the user is changing the DebouncedInput, causing the user's change to reset.

To solve the problem, I've wrapped DebouncedInput in a component to ignore the search text if it was the same as what onChange was triggered with

const React = require("react");

const SearchBox = React.createClass({
    propTypes: {
        value: React.PropTypes.string.isRequired,
        onChange: React.PropTypes.func.isRequired
    },
    getInitialState() {
        return { latestValue: this.props.value };
    },
    componentWillReceiveProps(nextProps) {
        const self = this;
        // Value we receive isn't what we set.
        if (self.state.latestValue !== nextProps.value) {
            self.setState({ latestValue: nextProps.value });
        }
    },
    shouldComponentUpdate(nextProps, nextState) {
        return this.state.latestValue !== nextState.latestValue;
    },
    onChange (e) {
        const self = this;
        self.setState({ latestValue: e.target.value });
        self.props.onChange(e);
    },
    render () {
        const self = this;
        return (
            <div className={"form-inline " + self.props.className}>
                <label>Search {}
                    <DebounceInput className="form-control" minLength={0} debounceTimeout={300} value={self.state.latestValue} onChange={self.onChange} />
                </label>
            </div>
        );
    }
});

module.exports = SearchBox;

@burtyish
Copy link

burtyish commented Aug 23, 2016

@nkbt the scenario you described in your latest comment is exactly what I've run into.

If you want to keep it in redux store and have "controllable" input with instant updates, then why do you need debounced-input in the first place? What is the desired behaviour for you?

In my application, I'm using redux and I expect the input's visible value to reflect the app (redux) state.
I want to keep the server updated with the values that the user enters. But I'd like to avoid sending a request for each change. That's why I'm debouncing - to update the server once the user has stopped (or paused) typing.

Here's a possible API that I came up with:

<DebounceInput 
    debounceTimeout={300} 
    value={this.props.value} 
    onChange={this.handleDebouncedChange} // Asynchronously update the server
    onChangeImmediate={this.handleImmediateChange } // Update the app state
/>

What do you think?
I'd hate to have to go and implement my own debouncing logic when you've already done such a fine job here.
Frankly, I don't see how the component could be used in a flux-style architecture as-is, but maybe I'm missing something.

UPDATE: Edited the API to be backwards-compatible

@burtyish
Copy link

Try to use defaultValue instead (usual one for any uncontrollable input in React)

I tried to use defaultValue with no value, but that doesn't work. If there's no value prop, then the initial state is set to an empty string. So the defaultValue is never rendered in the input:

getInitialState() {
    return {
      value: this.props.value || ''
    };
  }

Source: https://github.com/nkbt/react-debounce-input/blob/master/src/Component.js#L38

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

No branches or pull requests

6 participants