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.

Tried to synchronously call function {onBlur} from a different thread

See original GitHub issue

Description

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

Expo-snacks

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:closed
  • Created 2 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

4reactions
terrysahaidakcommented, Oct 5, 2021

You’re still calling onBlur right there.

It should be next:

runOnJS(onBlur)(isFinished)

Basically, runOnJS accepts the function, and then in the returning closure, you can call it with params.

1reaction
terrysahaidakcommented, Oct 5, 2021

@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.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Error in Reanimated "tried to synchronously call function res ...
Basically onStart, onActive and onEnd are full-fledged worklets, i.e. javascript functions that will be executed on the UI Thread.
Read more >
SyntheticEvent - React
The onBlur event handler is called when focus has left the element (or left some element inside of it). For example, it's called...
Read more >
Using Classic ASP and AJAX to Check Availability of a ...
You can call a function either synchronously (which most developers are ... with asynchronous calls where a new thread is created and your...
Read more >
Backbone.js
The event string may also be a space-delimited list of several events. ... Backbone.sync is the function that Backbone calls every time it...
Read more >
Dispatching custom events - The Modern JavaScript Tutorial
If an event handler calls methods that trigger other events – they are processed synchronously too, in a nested fashion. Let's say we...
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