简体   繁体   中英

Reactjs lost touchmove after setState

I have a question about Reactjs's touch event.
When the touch moves I will call the setState to render new divs.
But if I keep moving on those new divs the touchmove event won't fire anymore.
Just like the gif below
在此处输入图片说明

If I moving out of those divs then it's working.
在此处输入图片说明

here's my sample code:

var Container = React.createClass({
    getInitialState: function () {
        return {
            index: 0,
            rows: this.getRowsByIndex(0)
        };
    },

    getRowsByIndex: function(index) {
        var rows = [];

        for (var i = index; i < index + 10; i++) {
            rows.push(i);
        }

        return rows;
    },

    handleTouchStart: function() {
        console.log('start')
    },

    handleTouchMove: function() {
        console.log('move')
        this.setState({
            index: this.state.index + 1,
            rows: this.getRowsByIndex(this.state.index + 1)
        });
    },

    handleTouchEnd: function() {
        console.log('end')
    },

    handleTouchCancel: function() {
        console.log('cancel')
    },

    render: function () {
        return (
            <div style={{ width: 250, height: 300, border: '1px solid' }}
                onTouchStart={this.handleTouchStart}
                onTouchMove={this.handleTouchMove}
                onTouchEnd={this.handleTouchEnd}
                onTouchCancel={this.handleTouchCancel}>
                <div>
                    {this.state.rows.map(function(item, index) {
                        return <div key={item} style={{ width: 100, backgroundColor: '#eee' }}>{item}</div>;
                    })}
                </div>
            </div>
        );
    }
});

For now I only know If I added the pointerEvents: none to the div, then it works. Does anyone have the same problem?

tl;dr

Change <div key={item} to <div key={index} .

Demo: https://stackblitz.com/edit/react-5yqumb


A bit longer explanation

You need to change this part

{this.state.rows.map(function(item, index) {
    return (
        <div key={item} style={{ width: 100, backgroundColor: '#eee' }}> 
            {item}
        </div>
    );
})}

to (look at key={index} ):

{this.state.rows.map(function(item, index) {
    return (
        <div key={index} style={{ width: 100, backgroundColor: '#eee' }}> 
            {item}
        </div>
    );
})}

If you had changed this

<div key={index} style={{ width: 100, backgroundColor: '#eee' }}> 
    {item}
</div>

to another React component like:

class Box extends Component {
  componentDidMount() {
    console.log('mounted <Box />', this.props.children);
  }

  componentWillUnmount() {
    console.log('unmounted <Box />', this.props.children);
  }

  render() {
    return <div style={{ width: 100, backgroundColor: '#eee' }}>{this.props.children}</div>;
  }
}

You could have observed easily that on every state update, you mount and unmount <Box /> components. Look at these console.logs

mounted <Box /> 0
mounted <Box /> 1
mounted <Box /> 2
mounted <Box /> 3
mounted <Box /> 4
mounted <Box /> 5
mounted <Box /> 6
mounted <Box /> 7
mounted <Box /> 8
mounted <Box /> 9
start
unmounted <Box /> 0
mounted <Box /> 10
unmounted <Box /> 1
mounted <Box /> 11
unmounted <Box /> 2
mounted <Box /> 12
unmounted <Box /> 3
mounted <Box /> 13
unmounted <Box /> 4
mounted <Box /> 14
unmounted <Box /> 5
mounted <Box /> 15
unmounted <Box /> 6
mounted <Box /> 16
unmounted <Box /> 7
mounted <Box /> 17
unmounted <Box /> 8
mounted <Box /> 18

Removing and adding dom nodes is the root of the problem. In your case you want to have the same number of <div /> in the DOM and updates just their content so using index in that specific context is safe. You can read more about keys in the official documentation - Lists and Keys - React .

When you change <div key={item} to <div key={index} , console.logs look like this:

mounted <Box /> 0
mounted <Box /> 1
mounted <Box /> 2
mounted <Box /> 3
mounted <Box /> 4
mounted <Box /> 5
mounted <Box /> 6
mounted <Box /> 7
mounted <Box /> 8
mounted <Box /> 9
start
end

You can always try and set breakpoint in the DOM to see that DOM nodes are removed.

with key={index} 键={索引}

with key={item} 键={item}

As far as I know, that is because the touchmove and touchend should be dispatched from the same active touch [1];

That is, once you removed (due to React re-rendering, in this case) the element where touchstart being initiated, it will not further dispatch touchmove and touchend event.

That's why pointer-events: none seems to make it work, because you just bypassed the element which could be removed.

[1] https://www.w3.org/TR/touch-events/#event-touchmove

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM