Question: Focus input after redux dispatch finished

Question

Focus input after redux dispatch finished

Answers 3
Added at 2016-12-28 17:12
Tags
Question

I am dynamically creating list of inputs with React and Redux. After clicking a button an input is added to the end of list. I need to focus last added input. I tried this code but it focuses penultimate input

const mapDispatchToProps = (dispatch, ownProps) => ({
    onOptionsChange: (newOptions) => {
        dispatch(formActions.updateOptions(newOptions));
    }
});

...
this.props.onOptionsChange({ ...this.props, inputsList}); // change list of inputs
ReactDOM.findDOMNode(this.inputs[this.props.choices.length - 1]).focus();

In logs I can see that focus() is executed before props from state are updated. How can I wait for dispatch to finish?

Answers to

Focus input after redux dispatch finished

nr: #1 dodano: 2016-12-28 18:12

Create new wrapping component for input element. That component will autofocus, when rendered.

class Input extends React.Component {
    keepRef = (ref) => {
        this.ref = ref;
    }

    componentDidMount() {
        this.ref.focus();
    }

    render () {
        return (
            <input ref={this.keepRef} {...this.props} />
        )
    }
}

If you don't want Input component to handle focus, move logic to it's parent.

nr: #2 dodano: 2017-01-02 22:01

I would implement componentDidUpdate and check the length of your "input-array" or whatever data-structure you are using:

componentDidUpdate(prevProps, prevState) {
   if (prevProps.choices.length < this.props.choices.length) {
      ReactDOM.findDOMNode(this.inputs[this.props.choices.length - 1]).focus();
   }
}
nr: #3 dodano: 2017-01-03 07:01

I faced a similiar problem, wherein, I had a list of items and I need to focus on the last element whenever a new item is added. The below code is using flux architecture but i guess it should solve your problem.

addNewItem: function() {
    var self = this;
    var list = this.state.list || {};
    var listOrder = this.state.listOrder || [];
    var newKey = "some_random_key";

    list[newKey] = {
        "product": ""
    };
    listOrder.push(newKey);
    this.state.list = list;
    this.state.listOrder = listOrder;
    this.setState({list: this.state.list, listOrder: this.state.listOrder, showSaveButton: true});
    setTimeout(function(){
        $(".buy-form-item-list-row .buy-form-item-list-item-name").last().focus();
    }, 0);
},

So in the above code.. listOrder contains the keys of list of items. Eg: list is an object which contain the item. Eg:

listOrder=[ran_1, ran_2];

list={ ran_1: { product: "Eggs - 5" }, ran_2: { product: "Bread - 1" } }

render method, iterate over the listOrder and creates a Input tag with className="buy-form-item-list-item-name" for each item.

The trick to focus on the last item is the setTimeout with an interval of 0. When you call setState it does not trigger render function at that moment itself, instead it waits for the function to execute completely.

Hope this helps.

Source Show
◀ Wstecz