question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

State.END kept firing

See original GitHub issue

I’m trying to implement drag and drop feature using react-native-reanimated + react-native-gesture-handler. Everything is working fine except an event. The problem is an event (State.END) not stop triggering. Here is my code:

import * as React from "react";
import { RecyclerListView, LayoutProvider, DataProvider } from "recyclerlistview";
import Animated from "react-native-reanimated";
import { LayoutChangeEvent, Dimensions } from "react-native";
import { PanGestureHandler, State } from "react-native-gesture-handler";

const { cond, eq, add, call, Value, event, or } = Animated;

interface Props<T> {
	rowHeight: number;
	data: T[];
	indexToKey: (index: number) => string;
		renderRow: (
		data: T,
		index: number,
		state: "normal" | "dragging" | "placeholder",
		dragHandle: JSX.Element
	) => JSX.Element | null;
	renderDragHandle: () => JSX.Element;
	onSort: (newData: T[]) => void;
}

interface RState {
	dataProvider: DataProvider;
	dragging: boolean;
	draggingIdx: number;
}

function immutableMove(arr, from, to) {
	return arr.reduce((prev, current, idx, self) => {
		if (from === to) {
			prev.push(current);
		}

		if (idx === from) {
			return prev;
		}

		if (from < to) {
			prev.push(current);
		}

		if (idx === to) {
			prev.push(self[from]);
		}

		if (from > to) {
			prev.push(current);
		}
		return prev;
	}, []);
}

export class SortableList<T> extends React.PureComponent<Props<T>, RState> {
	list = React.createRef<RecyclerListView<any, any>>();
	_layoutProvider: LayoutProvider;
	rowCenterY: Animated.Node<number>;
	absoluteY = new Value(0);
	gestureState = new Value(-1);
	onGestureEvent: any;
	halfRowHeightValue: Animated.Value<number>;
	currIdx = -1;
	scrollOffset = 0;
	flatlistHeight = 0;
	topOffset = 0;
	scrolling = false;

	constructor(props: Props<T>) {
		super(props);

		this.halfRowHeightValue = new Value((-props.rowHeight / 2) - 190);
		const { width } = Dimensions.get("window");

		this.onGestureEvent = event([{
			nativeEvent: {
				absoluteY: this.absoluteY,
				state: this.gestureState
			}
		}]);

		this.rowCenterY = add(this.absoluteY, this.halfRowHeightValue);

		this._layoutProvider = new LayoutProvider(
			index => {
				return 1;
			},
			(type, dim) => {
				dim.width = width;
				dim.height = props.rowHeight;
			}
		);

		const dataProvider = new DataProvider((row1, row2) => {
			return row1.borrower_id !== row2.borrower_id;
		}, props.indexToKey);

		this.state = {
			dataProvider: dataProvider.cloneWithRows(props.data),
			dragging: false,
			draggingIdx: -1
		};
	}

	componentDidUpdate(prevProps) {
		if (prevProps.data !== this.props.data) {
			this.setState({
				dataProvider: this.state.dataProvider.cloneWithRows(this.props.data)
			});
		}
	}

	handleScroll = (_, __, offsetY: number) => {
		this.scrollOffset = offsetY;
	};

	handleLayout = (e: LayoutChangeEvent) => {
		this.flatlistHeight = e.nativeEvent.layout.height;
		this.topOffset = 190;
	};

	yToIndex = (y: number) =>
		Math.min(
			this.state.dataProvider.getSize() - 1,
			Math.max(
				0,
				Math.floor(
					(y + this.scrollOffset - this.topOffset) / this.props.rowHeight
				)
			)
		);

	moveList = amount => {
		if (!this.scrolling) {
			return;
		}

		this.list.current.scrollToOffset(
			this.scrollOffset + amount,
			this.scrollOffset + amount,
			false
		);

		requestAnimationFrame(() => {
			this.moveList(amount);
		});
	};

	updateOrder = y => {
		const newIdx = this.yToIndex(y);
		if (this.currIdx !== newIdx) {
			this.setState({
				dataProvider: this.state.dataProvider.cloneWithRows(
					immutableMove(
						this.state.dataProvider.getAllData(),
						this.currIdx,
						newIdx
					)
				),
				draggingIdx: this.yToIndex(y)
			});
			this.currIdx = newIdx;
		}
	};

	start = ([y]) => {
		this.currIdx = this.yToIndex(y);
		this.setState({ dragging: true, draggingIdx: this.currIdx });
	};

	reset = () => {
		const newData = this.state.dataProvider.getAllData();
		this.setState({
			dataProvider: this.state.dataProvider.cloneWithRows(newData),
			dragging: false,
			draggingIdx: -1
		});
		this.scrolling = false;
		this.currIdx = -1;
		console.log('entered');
		// this.props.onSort(newData);
	};

	move = ([y]) => {
		if (y + 100 > this.flatlistHeight) {
			if (!this.scrolling) {
				this.scrolling = true;
				this.moveList(20);
			}
		} else if (y < 100) {
			if (!this.scrolling) {
				this.scrolling = true;
				this.moveList(-20);
			}
		} else {
			this.scrolling = false;
		}
		this.updateOrder(y);
	};

	_rowRenderer = (type, data, index) => {
		return this.props.renderRow(
			data,
			index,
			this.state.draggingIdx === index ? "placeholder" : "normal",
			<PanGestureHandler
				maxPointers={1}
				onGestureEvent={this.onGestureEvent}
				onHandlerStateChange={this.onGestureEvent} >
				<Animated.View>{this.props.renderDragHandle()}</Animated.View>
			</PanGestureHandler>
		);
	};

	render() {
		const { dataProvider, dragging, draggingIdx } = this.state;

		return (
			<>
				<Animated.Code>
					{() =>
						cond(
							eq(this.gestureState, State.BEGAN),
							call([this.absoluteY], this.start)
						)
					}
				</Animated.Code>
				<Animated.Code>
					{() =>
						cond(
							or(
								eq(this.gestureState, State.END),
								eq(this.gestureState, State.CANCELLED),
								eq(this.gestureState, State.FAILED),
								eq(this.gestureState, State.UNDETERMINED)
							),
							call([], this.reset)
						)
					}
				</Animated.Code>
				<Animated.Code>
					{() =>
						cond(
							eq(this.gestureState, State.ACTIVE),
							call([this.absoluteY], this.move)
						)
					}
				</Animated.Code>
				{dragging ? (
					<Animated.View
						style={{
							top: this.rowCenterY,
							position: "absolute",
							width: "100%",
							zIndex: 99,
							elevation: 99
						}} >
						{this.props.renderRow(
							dataProvider.getDataForIndex(draggingIdx),
							draggingIdx,
							"dragging",
							this.props.renderDragHandle()
						)}
					</Animated.View>
				) : null}

				<RecyclerListView
					ref={this.list}
					style={{ flex: 1 }}
					onScroll={this.handleScroll}
					onLayout={this.handleLayout}
					layoutProvider={this._layoutProvider}
					dataProvider={dataProvider}
					rowRenderer={this._rowRenderer}
					extendedState={{ dragging: true }} />
			</>
		);
	}
}

and here is my dependencies:

  "dependencies": {
    "@react-native-community/async-storage": "^1.7.1",
    "@react-native-community/datetimepicker": "^2.1.0",
    "@react-native-community/netinfo": "^5.5.0",
    "@sendgrid/mail": "^6.4.0",
    "native-base": "^2.13.8",
    "react": "16.9.0",
    "react-native": "0.61.2",
    "react-native-gesture-handler": "^1.4.1",
    "react-native-linear-gradient": "^2.5.6",
    "react-native-loading-spinner-overlay": "^1.0.1",
    "react-native-material-dropdown": "^0.11.1",
    "react-native-material-menu": "^1.0.0",
    "react-native-reanimated": "^1.7.0",
    "react-native-screens": "^2.4.0",
    "react-native-searchbar": "^1.16.0",
    "react-native-vector-icons": "^6.6.0",
    "react-navigation": "^4.0.10",
    "react-navigation-stack": "^1.9.4",
    "realm": "^4.0.0-beta.0",
    "recyclerlistview": "^3.0.0"
  },

The State.END keep firing the this.reset . Can anyone help me out! Thank you.

Note: (this code working fine in Expo. Seeing this problem on RN Cli, And i’m testing on real android device)

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (1 by maintainers)

github_iconTop GitHub Comments

4reactions
tr3v3rcommented, Apr 5, 2020

The same here. It seems that after END handler should switch to UNDETERMINED, but actually he doesn’t.

1reaction
MutatedBreadcommented, Feb 2, 2021

Please create a new issue with the reproduction example.

I will find sometime to do that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

UPDATE: State court halts firing-squad execution with ...
WASHINGTON (CNS) — South Carolina's Supreme Court has issued a temporary stay blocking the state from executing death-row inmate Richard Moore by firing...
Read more >
Wrongful Termination: Was Your Firing Illegal?
Knowing if your firing was legal or illegal can be a complicated issue. Employers usually may fire you at any time but there...
Read more >
Governor Newsom and CAL FIRE Announce the End of Peak ...
NAPA – Today, at a fire station in Napa, Governor Gavin Newsom and state fire officials announced the end of peak fire season...
Read more >
The American Holiday Tradition of Firing Workers for No ...
There is one irredeemably pinko state where, excepting a probationary period that typically lasts one year, your employer must show cause to get ......
Read more >
Fire discipline
Fire discipline is a system of communication in the military, primarily for directing artillery. By definition, fire discipline is the language of fire...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found