Is there any way to manage onPanResponderMove? - react-native

Hello I'm working on react native and I have this code to manage a drag & drop on a element
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove :
Animated.event([null, {
dx: this.state.pan.x,
dy: this.state.pan.y
}]),
.
.
.
The thing is that I want to be able to manage the Animated.event pass through the onPanResponderMove when I press a button
Something like
If I press a button then OnPanResponderMove so I can use drag & drop
I don't really know how I can do it. Has anybody any key on this?

I am not 100% sure but probably you can just edit the onStartShouldSetPanResponder: () => true. Right now it always returns true but as the function name says maybe you must return wether you like to enable it or not.
So the solution should be something like this:
onStartShouldSetPanResponder: function () {
return this.state.panResponderEnabled
}
And else where you have a button with an onPress property (or anything alike) where you can actually change the state.

Related

React Native state update during animation "resets" the animation

I am facing a problem that I've tried to solve in lots of different ways, but I cannot get it to work. Please see this Expo application, I've created a dumb example that demonstrates my problem: https://snack.expo.io/HJB0sE4jS
To summarize, I want to build an app with a draggable component (The blue dot in the example), but while the user drags the component I also need to update the state of the app (the counter in the example). The problem is that whenever the state updates during dragging, the component resets to it's initial position. I want to allow the user to freely drag the component while state updates happen.
I was able to "solve" the issue by putting the PanResponder in a useRef, so it won't be reinitialized in case of a state update, but as you can see in the example, I want to use the state in the PanResponder. If I put it in a useRef I cannot use the state in the PanResponder because it will contain a stale value (it will always contain the initial value of the counter which is 0).
How do you handle these kind of situations in react native? I guess it is not too uncommon that someone wants to update the state during an animation, although I cannot find any documentation or examples on this.
What am I doing wrong?
Edit: I was investigating further and I can see that the problem is that I'm mapping the (dx,dy) values from the gesture parameter to the position, but the (dx,dy) values are reset to (0,0) when the state changes. I guess (dx,dy) initialized to (0,0) when PanResponder is created. Still don't know what to do to make this work...
A mutable ref that holds the latest counter state value, along with a ref to prevent re-initializing the PanResponder should solve the problem in the example:
const [counter] = useCounter();
// Update the counterValue ref whenever the counter state changes
const counterValue = useRef(counter);
useEffect(() => {
counterValue.current = counter;
}, [counter]);
const position = useRef(new Animated.ValueXY());
const panResponder = useRef(PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event(
[null, { dx: position.current.x, dy: position.current.y }],
{ listener: () => console.log(counterValue.current) } // counterValue being a ref, will not go stale
),
onPanResponderRelease: () => {
Animated.spring(position.current, { toValue: { x: 0, y: 0 } }).start();
}
})
);
You can check the above suggestion here: https://snack.expo.io/rkMLgp4jB
I understand though that this is a rather simplified example, and may not work for your actual use-case. It would help if you could share some more details on the actual usage!

Vuetify, how to close drawer?

I have added navigation to the drawer, everything works fine except the case when the link is the same as the page is. The drawer left open after clicking on the link and it isn't obvious for the visitor that the page is the current page.
I have tried to do something like this #click.stop="(this.router.path != '/' ?
this.router.push('/') : drawer = !drawer)" Vue doesn't rapport any mistake and the code doesn't work.
Where am I wrong?
The drawer data key is looking for a boolean, if it's truthy the navigation drawer will show. So, you can add #click="drawer = false" to your menu links, and it will close the draw when any link is clicked.
Example in the docs: https://vuetifyjs.com/components/navigation-drawers#example-6
I handled this by making the drawer in my app a child of the route that uses it. The isOpen property is managed in the parent. I pass isOpen as a prop to the drawer and emit open and close events as appropriate.
Oh, I also found that timeouts are necessary to ensure the open / close animations work correctly. Someone please let me know if you found a better way to handle animations as this feels a little wonky.
I handle a few other things, like right/left justify and a return route, but ignore the noise if it isn't helpful.
Here's a parent loading the component
<my-drawer
:iconName="'my_icon'"
:isOpen="drawerIsOpen"
:justify="'right'"
:returnRoute="'home'"
#close="drawerIsOpen = false"
#open="drawerIsOpen = true"
>
// ...
</my-drawer>
Here are the methods from within the drawer component:
data() {
return {
closeDelay: 500,
width: 0,
};
},
methods: {
closeBtnClick() {
this.$emit('close');
setTimeout(() => { this.$router.push(this.returnRoute); }, this.closeDelay);
},
mounted() {
setTimeout(() => { this.$emit('open'); }, this.closeDelay);
},

How to implement, React Native Drag-n-Drop

I want to implement react-native drag-and-drop, to swap component on the drop. supposed there is five component on a screen once I drag it should highlight dropable element. and once it is dropped the component should be swap.
In order to implement drag an drop you need to use the pan responders. First you need to define where are you going to store the values when the object is moving, you can set a property on the state.
state = {
pan: new Animated.ValueXY(),
};
Then you will have to create the pan responders in componentWillMount for example:
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder : () => true,
onPanResponderMove : Animated.event([null,{ // <--- When moving
dx : this.state.pan.x,
dy : this.state.pan.y
}]),
onPanResponderRelease : (e, gesture) => {} // <--- callback when dropped
});
Finally you need to set the left and top to the animated element in the render method.
<Animated.View
{...this.panResponder.panHandlers}
style={[this.state.pan.getLayout(), styles.circle]}>
<Text style={styles.text}>Drag me!</Text>
</Animated.View>
The this.state.pan.getLayout() returns the top and left, you need this in order to move the element around.
In order to swap the elements you need to implement the onPanResponderRelease.
For more detailed steps you can take a look at this tutorial: https://moduscreate.com/blog/animated_drag_and_drop_with_react_native

How to handle a button press inside react-navigation titlebar

I am pretty new to react-native and I am currently handling navigation for my app using react-navigation.
I want to be able to add a button to my app's title bar created by react-navigation and be able to press a button to call a function.
static navigationOptions = ({ navigation }) => {
const { params } = navigation;
return {
title: 'Assign To',
headerRight: (
<Button
title='Back'
onPress={() => params.handlePress()}
/>
),
};
};
This is close to what I read from the react-navigation docs, but it I keep getting an error of undefined is not an object.
componentDidMount() {
this.props.navigation.setParams({ handlePress: () => this.myFunction.bind(this) });
}
I have myFunction defined inside my component as follows
myFuntion() {
//...
}
Any help is highly appreciated
I'm going to answer this, with a little more explanation. The problem as you know was that you were combining the syntax of two similar approaches. You can use either the .bind syntax: handlePress: this.myFunction.bind(this) or the so-called fat-arrow syntax: handlePress: () => this.myFunction. They are more-or-less equivalent and there are opinions that the fat-arrow syntax supersedes the bind syntax (and, as always, opinions to the contrary :) ). There are differences, such as (quoting from this):
Arrow functions are always anonymous, which means you can't, for
instance, reliably call them recursively (since you don't have a
reliable lexical name to use).
Arrow functions actually create
lexical bindings for this, super, and arguments. Only this is bound
by .bind(..).
Arrow functions cannot be used in new expressions,
while bind(..) bound functions can.
Also, given that this is a react native question, it might help to be aware of this interesting article:
https://medium.freecodecamp.org/why-arrow-functions-and-bind-in-reacts-render-are-problematic-f1c08b060e36
For most elements you can use the onPress() function in react-native
onPress={() => { Alert.alert("You clicked me") } }
If you need to bind you can usually do it with the following example:
onPress={this.functionName.bind(this)}

Can I use the native animation driver to track a pan gesture?

The React-Native Animated docs show this example as a way to make an Animated.Value (_panX) track a pan gesture:
onPanResponderMove: Animated.event([
null, // raw event arg ignored
{dx: this._panX}, // gestureState arg
]),
Likewise, the Animated.event docs say I can pass a second config parameter, with:
useNativeDriver: Uses the native driver when true. Default false.
Unfortunately, doing this appears to be broken:
onPanResponderMove: Animated.event(..., { useNativeDriver: true });
In particular, because PanResponder.create has a onResponderMove function that does:
if (config.onPanResponderMove) {
config.onPanResponderMove(e, gestureState);
}
Which causes an error, when it treats config.onPanResponderMove as a function, when it's actually an object...
Am I doing something wrong? Or is useNativeDriver not supported when making Animated.Value track a gesture? Would be nice to have something seamlessly track the mouse like that...