Requirement: I have to blink a View for 2 sec with 2 different color (for ex red and white).
I can do this by using this code -
const [state, setState] = React.useState(false)
const [initialState, setInitialState] = React.useState(0)
React.useEffect(() => {
if (initialState < 2){
let interval = setInterval(() => {
setState(true)
setInitialState(initialState + 1)
setTimeout(() => {
setState(false)
}, 80);
}, 300);
setTimeout(() => {
clearInterval(interval)
}, 600);
}
}, [initialState])
and called it like -
<View style={{...styles.mainContainer, backgroundColor: state ? Colors.GRO7 : Colors.GRC9}}>
Another Requirement: I have another screen from it i an change the address, on successful address change i have to blink this view again for 2 sec. I'm not sure where i can reset the initial value to 0 again.
I am new In react native, could some one guide me how to achieve this functionality
Can‘t completely understand what‘s your target.
Here is a blinking text sample
you can pass in the initial value to this component instead of defining 0 in this line:
const [initialState, setInitialState] = React.useState(0)
you could have a param to pass in and put it instead of 0 so that every time that param changes this component will re render. and so you get a new initial state.
for example:
const [initialState, setInitialState] = React.useState(initialValue)
Related
I am trying to animate an image in and out when clicked using React Native reanimated, but the JSX if else condition is not working quite right. Below is the code that works, but only works when clicked for the first time. The state toggled is set to true, so when clicked again it should set the size back to the original and the image should animate back to it and vice versa.
The setup is like so:
export default () => {
const newNumber = useSharedValue(100);
const [Toggled, setToggled] = useState(false);
const style = useAnimatedStyle(() => {
return {
width: withSpring(newNumber.value),
height: withSpring(newNumber.value, { damping: 10,
stiffness: 90, }),
};
});
onPress={() => {
{Toggled ? newNumber.value = 100 : newNumber.value = 350; setToggled(true)}
}}
The problem is when I try to add the newNumber.Value = 100 when setting Toggled to false, it gives me an error. I try to add it like this :
{Toggled ? newNumber.value = 100; setToggled(false) : newNumber.value = 350; setToggled(true)}
Why does it accept the first one but not the second?
If I use this, it works, but can it be done the other way?
const isToggled = Toggled;
if (isToggled) {
// alert('Is NOT Toggled');
newNumber.value = 100;
setToggled(false)
} else {
// alert('Is Toggled');
newNumber.value = 350;
setToggled(true)
}
Thanks
It looks you are just setting a value to newNumber base on Toggled, and then setting the Toggled to its opposite value. So why not do
onPress={() => {
newNumber.value = Toggled ? 100 : 350
setToggled(prev=>!prev)
}}
I'm using React Native and Reanimated and I want an animation to play after 2 seconds.
When a user moves a card, the card should stay at it's new position for 2 seconds, and then move back to it's place.
This is what I have:
const panGesture = useAnimatedGestureHandler<PanGestureHandlerGestureEvent>({
onActive: event => {
translateX.value = event.translationX;
if (event.translationX <= 0) {
// disabling swiping from right to left
translateX.value = 0;
}
},
onEnd: event => {
const shouldStick = translateX.value >= TRANSLATE_X_THRESHOULD;
if (shouldStick) {
translateX.value = withTiming(120);
runOnJS(moveBack)(translateX);
} else {
translateX.value = withTiming(0);
}
},
});
I tried using setTimeOut to count 2 seconds, and then update translateX but I get this error:
undefined is not an object (evaluating 'fun.__callAsync')
This is moveBack function:
const moveBack = (translateX: Animated.SharedValue<number>) => {
console.log("TRANSLATEX: " + translateX);
setTimeout(() => {
translateX.value = 0;
}, 2000);
}
I don't even see the TRANSLATEX log, so I guess it won't even get there.
I can't really figure out what's the problem or how to word it so I can find a solution.
The solution was way easier than I thought.
I'm using Reanimated 2.2.0 and there is withDelay option to add to the animation and it works great.
This is what I added after translateX.value = withTiming(120); (instead of the runOnJs line):
translateX.value = withDelay(2000, withTiming(0));
So right after setting translateX to 120, it waits 2 seconds, and then setting the value back to 0.
when using runOnJS
const doSomething = () => {
...
} // declare arrow function before runOnJS
runOnJS(doSomething)(arg)
function doSomething(){
...
} // can declare normal function before/after runOnJS coz of hoisting
I am trying to hide and show the header based on the scroll event from reanimated 2 useAnimatedScrollHandler and I need to use the diffClamp so whenever the user scrolls up the header should be shown in less time than the whole scroll event contentOffset.y value but the problem is diffClamp is I think from reanimated v1 and I need to use useAnimatedStyle hook in order to animate styles in reanimated v2 and finally it gives an error.
Can someone please help with it?
const clamp = (value, lowerBound, upperBound) => {
"worklet";
return Math.min(Math.max(lowerBound, value), upperBound);
};
const scrollClamp = useSharedValue(0);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event, ctx) => {
const diff = event.contentOffset.y - ctx.prevY;
scrollClamp.value = clamp(scrollClamp.value + diff, 0, 200);
},
onBeginDrag: (event, ctx) => {
ctx.prevY = event.contentOffset.y;
}
});
const RStyle = useAnimatedStyle(() => {
const interpolateY = interpolate(
scrollClamp.value,
[0, 200],
[0, -200],
Extrapolate.CLAMP
)
return {
transform: [
{ translateY: interpolateY }
]
}
})
I need to perform different action on single and double tap on a view. On double tap I need to like the image just like Instagram double tap experience. On single tap I need to open a modal.
For double tap I have used TapGestureHandler which works perfect
<TapGestureHandler
ref={this.doubleTapRef}
maxDelayMs={200}
onHandlerStateChange={this.onProductImageDoubleTap}
numberOfTaps={2}
>
<SomeChildComponent ...
But when I add any Touchable to detect single tap in the
<TouchableWithoutFeedback onPress={this.imageTapped}>
on double tapping the this.imageTapped function is called twice along with this.onProductImageDoubleTap. Is there any way to cancel tap on touchable when two taps are done is quick succession
The best solution is not using state, since setting state is asynchronous.Works like a charm for me on android !
let lastPress = 0;
const functionalComp = () => {
const onDoublePress = () => {
const time = new Date().getTime();
const delta = time - lastPress;
const DOUBLE_PRESS_DELAY = 400;
if (delta < DOUBLE_PRESS_DELAY) {
// Success double press
console.log('double press');
}
lastPress = time;
};
return <View
onStartShouldSetResponder =
{(evt) => onDoublePress()}>
</View>
}
2022 update
This is a performant native solution without any JS thread blocking calculation!
Many more tips here
const tap = Gesture.Tap()
.numberOfTaps(2)
.onStart(() => {
console.log('Yay, double tap!');
});
return (
<GestureDetector gesture={tap}>
{children}
</GestureDetector>
);
The best solution is use react-native-gesture-handler
https://github.com/software-mansion/react-native-gesture-handler
Here is my solution -
import {State, TapGestureHandler} from 'react-native-gesture-handler';
export const DoubleTap = ({children}: any) => {
const doubleTapRef = useRef(null);
const onSingleTapEvent = (event: any) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log("single tap 1");
}
};
const onDoubleTapEvent = (event: any) => {
if (event.nativeEvent.state === State.ACTIVE) {
console.log("double tap 1");
}
};
return (
<TapGestureHandler
onHandlerStateChange={onSingleTapEvent}
waitFor={doubleTapRef}>
<TapGestureHandler
ref={doubleTapRef}
onHandlerStateChange={onDoubleTapEvent}
numberOfTaps={2}>
{children}
</TapGestureHandler>
</TapGestureHandler>
);
};
Now we will wrap the component where we need to detect the double and single tap : -
<DoubleTap>
<View>
...some view and text
</View>
</DoubleTap>
The package react-native-double-tap seems to be what you are looking for.
since you are asking on handling one tap and double tap, here's a simple code i think should covered your issue
Untested
first defined clickCount:0 in state:
state={clickCount:0, //another state}
then create a function with setTimeout to handling if user tapping once or two times:
handlingTap(){
this.state.clickCount==1 ?
//user tap twice so run this.onProductImageDoubleTap()
this.onProductImageDoubleTap :
//user tap once so run this.imageTapped with setTimeout and setState
//i set 1 sec for double tap, so if user tap twice more than 1 sec, it's count as one tap
this.setState({clickCount:1}, ()=>{
setTimeout(()=>{
this.setState({clickCount:0})
this.imageTapped()
}, 1000)
})
}
<TouchableWithoutFeedback onPress={this.handlingTap()}/>
just used TouchableWithoutFeedback instead of TapGestureHandler
With hooks:
const [lastPressed, setLastPressed] = useState(0);
const handlePress = useCallback(() => {
const time = new Date().getTime();
const delta = time - lastPressed;
setLastPressed(time);
if (lastPressed) {
if (delta < DOUBLE_PRESS_DELAY) {
console.log('double press');
} else {
console.log('single press');
}
}
}, [lastPressed]);
I have modified flix's answer into this. By this way, you can catch one and double click separately. I also changed debounce into 300ms which is fairly well for one and double click.
state={clickCount:0, //another state}
Binding context into the handlingTap method
constructor(props){
super(props)
this.handlingTap = this.handlingTap.bind(this)
}
With this function you can catch them separately
handlingTap() {
this.state.clickCount === 1
? this.doubleClick() // This catches double click
: this.setState(state => ({ clickCount: state.clickCount + 1 }), () => {
setTimeout(() => {
if (this.state.clickCount !== 2) {
this.oneClick() // this catches one click
}
this.setState({ clickCount: 0 })
}, 300)
})
}
In the button you can use this way
<TouchableWithoutFeedback onPress={this.handlingTap}></TouchableWithoutFeedback>
I make us several Switch Components in one view. When I switch on one switch, I want all others to switch off. Currently, I set the boolean value property via the state. This results in changes happen abruptly because the switch is just re-rendered and not transitioned.
So how would you switch them programmatically?
EDIT 2: I just discovered that it runs smoothly on Android so it looks like an iOS-specific problem.
EDIT: part of the code
_onSwitch = (id, switched) => {
let newFilter = { status: null };
if (!switched) {
newFilter = { status: id };
}
this.props.changeFilter(newFilter); // calls the action creator
};
_renderItem = ({ item }) => {
const switched = this.props.currentFilter === item.id; // the state mapped to a prop
return (
<ListItem
switchButton
switched={switched}
onSwitch={() => this._onSwitch(item.id, switched)}
/>
);
};