Tried to synchronously call function {onBlur} from a different thread
See original GitHub issueDescription
I have created one Custom animated scrolling bottom sheet. Then Animated View can move ups and down. The Animated View has two snaps points. It starts with top of the screen, if user move the Animated View down, it goes middle of the screen, if user again move again to bottom then it will go bottom of the screen. I made this Scrolling-Bottom-sheet as reuse-able Component. I import this Bottom sheet in the app Component and inside the Scrolling-Bottom-sheet I used custom search input Component. When search Input onFocus the KeyBoard PopUp. Currently, it behaves like this GIF.
UI/UX perspective, it does not look good when KeyBoard still showing when Scrolling-Bottom-sheet move all the way down.
I want to hide this KeyBoard when Scroll-Bottom-sheet goes all the way bottom. I have made one function which takes boolean value if it is true then it will trigger the Keyboard.dismiss()
function. I pass this function to Search Input’s onBlur
props.
I import this Function to the Scrolling-Bottom-sheet and passing to the useAnimatedGestureHandler's
bottom screen condition. When it come to that screen: I got the Error: Tried to synchronously call function {onBlur} from a different thread
Snack
Code
Scroll-bottom-sheet code
import React from "react";
import { StyleSheet, Dimensions, Keyboard } from "react-native";
import {
PanGestureHandler,
PanGestureHandlerGestureEvent,
TouchableOpacity,
} from "react-native-gesture-handler";
import Animated, {
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withTiming,
Easing,
} from "react-native-reanimated";
import styled from "styled-components/native";
import { onBlur } from "./SearchBar"; // IMPORT THE ONBLUR FUNCTION
interface Props {
children: React.ReactNode;
}
type ContextType = {
translateY: number;
};
const { height: SCREEN_HEIGHT } = Dimensions.get("screen");
const LoadingContainer = styled.View`
height: ${SCREEN_HEIGHT - 300}px;
background-color: #fff;
justify-content: center;
`;
const ScrollBottomSheet = ({ children }: Props) => {
const top = useSharedValue(SCREEN_HEIGHT);
const animatedStyle = useAnimatedStyle(() => {
"worklet";
return {
top: top.value * 0.1,
bottom: 0,
};
});
const gestureHandler = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
ContextType
>(
{
onStart(_, context) {
context.translateY = top.value;
},
onActive(event, context) {
top.value = context.translateY + event.translationY;
},
onEnd(event, _) {
if (event.y > 0 && event.y < 200) {
top.value = withTiming(SCREEN_HEIGHT * 3.8, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
} else if (event.y > 200) {
top.value = withTiming(SCREEN_HEIGHT * 7.05, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
// THIS IS CONDITION WHICH MAKE THE BOTTOM-SHEET ALL THE WAY DOWN. IN HERE I WANT TO HIDE THE
KEYBAORD.
onBlur(true);
} else if (event.translationY < 0) {
top.value = withTiming(SCREEN_HEIGHT, {
duration: 500,
easing: Easing.inOut(Easing.ease),
});
}
},
},
[top]
);
return (
<PanGestureHandler onGestureEvent={gestureHandler}>
<Animated.View style={[styles.container, animatedStyle]}>
<Animated.View style={styles.grbber} />
{children}
</Animated.View>
</PanGestureHandler>
);
};
export default ScrollBottomSheet;
const styles = StyleSheet.create({
container: {
position: "absolute",
left: 0,
right: 0,
top: 0,
backgroundColor: "#fff",
shadowOffset: {
height: -6,
width: 0,
},
shadowOpacity: 0.1,
shadowRadius: 5,
borderTopEndRadius: 15,
borderTopLeftRadius: 15,
},
grbber: {
width: 80,
height: 20,
alignSelf: "center",
marginTop: 5,
borderTopWidth: 5,
borderTopColor: "#aaa",
},
});
Search-component
import React, { useRef, useEffect, forwardRef } from "react";
import {
Animated,
Keyboard,
KeyboardTypeOptions,
TextInputProps,
TextInput,
Text
} from "react-native";
import styled from "styled-components/native";
type Props = TextInputProps & {
text: string;
hint: string;
onChangeText?: ((text: string) => void) | undefined;
onClearText: () => void;
animatedStyle?: { height: Animated.AnimatedInterpolation };
keyboardType?: KeyboardTypeOptions;
autoFocus?: boolean;
};
const Container = styled(Animated.View)`
flex-direction: row;
background-color: #e9dede;
border-radius: 6px;
align-items: center;
`;
const SearchInput = styled.TextInput`
flex: 1;
padding-right: 16px;
color: #000000;
font-size: 16px;
line-height: 20px;
height: 44px;
`;
// on Blur function
export const onBlur = (t: boolean): void => {
if (t) {
return Keyboard.dismiss();
} else {
return;
}
};
const SearchBar = ({
text,
hint,
onChangeText,
autoFocus,
onClearText,
keyboardType,
animatedStyle,
maxLength,
}: Props) => {
const searchInput = useRef<TextInput>(null);
const onSearchPress = () => {
searchInput?.current?.isFocused();
};
useEffect(() => {
if (autoFocus) {
onSearchPress();
}
}, [autoFocus]);
return (
<Container accessible={false} style={animatedStyle}>
<SearchInput
ref={searchInput}
onChangeText={onChangeText}
value={text}
placeholder={hint}
maxLength={maxLength}
underlineColorAndroid={"transparent"}
placeholderTextColor={"grey"}
keyboardType={keyboardType}
autoFocus={autoFocus}
onBlur={onBlur} // on Blur function
/>
</Container>
);
};
export default SearchBar;
Main App-component
import React, { useState, useEffect } from "react";
import { StyleSheet, Button } from "react-native";
import { TouchableHighlight } from "react-native-gesture-handler";
import MapView from "react-native-maps";
import styled from "styled-components";
import ScrollBottomSheet from "./ActionSheet";
import SearchBar, { onBlur } from "./SearchBar";
const initialRegion = {
latitudeDelta: 15,
longitudeDelta: 15,
latitude: 60.1098678,
longitude: 24.7385084,
};
export default function App() {
return (
<>
<MapView style={styles.mapStyle} initialRegion={initialRegion} />
<ScrollBottomSheet>
<SearchContainer>
<SearchBar hint={"search"} />
</SearchContainer>
<Button title="On Blur Button" onPress={() => onBlur(true)} />
</ScrollBottomSheet>
</>
);
}
const styles = StyleSheet.create({
mapStyle: {
flex: 1,
},
});
const SearchContainer = styled.View`
padding: 10px;
`;
Package versions
Tech | Version |
---|---|
react-native-gesture-handler | ^1.10.3 |
react-native-reanimated | ^2.2.0 |
react | 16.13.1 |
react-native | 0.63.4 |
java | 11.0.11 |
Affected platforms
- Android
- iOS
- Web
Issue Analytics
- State:
- Created 2 years ago
- Comments:6 (3 by maintainers)
You’re still calling
onBlur
right there.It should be next:
Basically, runOnJS accepts the function, and then in the returning closure, you can call it with params.
@alakdam07 could you please close this issue then? Also, if you have questions please use discussions instead. It helps us to keep the status of the bugs easier.