gitim / react-native-sortable-list

React Native Sortable List component
MIT License
885 stars 279 forks source link

flicker after one row removed? #87

Open callagga opened 6 years ago

callagga commented 6 years ago

Any advice here regarding a "flicker" I see across the whole list when I delete just one row from the list. I have tried the same thing with the react-native "flatlist" and I don't see this flicker. The more interesting thing also is that this "flicker" doesn't occur every time. When I say flicker, whilst the 4th item is being removed, when the flicker occurs I see the "redraw" of all items including the 1st/2nd/3rd too.

Reproducing: I took the basic example and added a button at the bottom to trigger removing the 4th item in the list. I should point out my testing is on the IOS simulator. Code below.

  onPressLearnMore() {
    console.log('onPressLearnMore', this.state.data);

    let newObject = {};
    Object.keys(this.state.data).forEach((k, index) => {
      if (index !== 3) {
        newObject[k] = this.state.data[k];
      };
    });

    this.setState({
      data: newObject,
    });

  };
rahmanmarija commented 6 years ago

Any updates on this?

Daimension commented 6 years ago

I think it's about uniqueRowKey.id, it changes in componentWillReceiveProps func, which leads to rerender because key has changed After some test and experiment, I find a way to make delete & add item without filcker possible. The basic idea is to manipulate order while the real order of data remains unchanged(reuse key).

I hope there's a better way. What I do gives me a headache :(

  1. stop changing uniqueRowKey.id, use you own keyExtractor.
  2. container pass order(manipulated, decr every order value by 1 if it's larger than the deletedOrder) and data(order unchanged, item added or removed) props to SortableList, container also need to pass deletedOrder(for example, order=[5, 4, 0, 1, 3, 2], pass 3 if you want to delete the 5th item, and the new order should be [4, 3, 0, 1, 2] ) prop when delete happens.
  3. update the related _rowsLayouts and _resolveRowLayout.
  4. the code below only handle one item change at a time.
  5. keep this.order up to date in _setOrderOnMove, somewhere near this.props.onChangeOrder (unnecessary)
  removeAndAdjustKey(source, deletedKey) {
    let result = {}
    deletedKey = parseInt(deletedKey);
    for (key in source) {
      key = parseInt(key);
      if (key < deletedKey) {
        result[key] = source[key];
      } else if (key > deletedKey) {
        let old = key - 1;
        result[old] = source[old];
      }
    }
    return result;
  }

  componentWillReceiveProps(nextProps) {
    let {order: nextOrder} = nextProps;

    let curOrder = this.order;
    if (curOrder && nextOrder && curOrder.length != nextOrder.length) {
      if (curOrder.length > nextOrder.length) {
        let deletedOrder = nextProps.deletedOrder;
        this._rowsLayouts = this.removeAndAdjustKey(this._rowsLayouts, deletedOrder);
        this._resolveRowLayout = this.removeAndAdjustKey(this._resolveRowLayout, deletedOrder);
      } else {
        let key = nextOrder[curOrder.length];
        this._rowsLayouts[key] = new Promise((resolve) => {
          this._resolveRowLayout[key] = resolve;
        });
      }

      LayoutAnimation.configureNext(LayoutAnimation.create(220,
          LayoutAnimation.Types.easeInEaseOut,
          LayoutAnimation.Properties.opacity
      ));
      this._onUpdateLayouts();
    }

    this.order = [...nextOrder];    
  }
Daimension commented 6 years ago

better