satya164 / react-native-tab-view

A cross-platform Tab View component for React Native
MIT License
5.13k stars 1.07k forks source link

TextInput auto blur on first click (Android only) #1181

Open devWaleed opened 3 years ago

devWaleed commented 3 years ago

Current behaviour

I have 2 tabs. First tab is active by default (i.e initial tab). Second tab has a TextInput. When I start app, click on second tab header it shows me second tab contents i.e the TextInput. When I click on the text field for typing it opens keyboard than auto closes for first time only. I am getting blur event on TextInput field. If I click again everything works fine.

Expected behaviour

On click on TextInput should open keyboard like normal.

Code sample

Simple Two tabs code with first as default open and second as inactive but having TextInput.

What have you tried

I have verified by putting blur event handler on TextInput. I am getting this event on Android only.

Your Environment

software version
ios or android Android 10
react-native 0.63.4
react-native-tab-view 3.0.1
react-native-pager-view 5.1.9
node 14.13.1
npm or yarn yarn
github-actions[bot] commented 3 years ago

Couldn't find version numbers for the following packages in the issue:

Can you update the issue to include version numbers for those packages? The version numbers must match the format 1.2.3.

The versions mentioned in the issue for the following packages differ from the latest versions on npm:

Can you verify that the issue still exists after upgrading to the latest versions of these packages?

devWaleed commented 3 years ago

Not using Expo but react-native-cli and pager-view version is 5.1.9 updated but still issue.

nathangabriel27 commented 3 years ago

Same problem here, I have 2 tabs when TextInput triggers onchange the keyboard automatically dismiss.

nathangabriel27 commented 3 years ago

Code example:

import React, { useState } from 'react';

import { View, useWindowDimensions, TextInput } from 'react-native';
import { TabView, SceneMap } from 'react-native-tab-view';

export default function TabViewExample() {
  const layout = useWindowDimensions();

  const SecondRoute = () => (
  <View style={{ flex: 1, backgroundColor: '#ff4081' }} />
);
const [enterprise_types, setEnterprise_types] = useState('test')
const FirstRoute = () => (
   <View style={{ flex: 1, backgroundColor: '#673ab7' }} >
  <TextInput
           keyboardType="decimal-pad"
            returnKeyType={'search'}
            value={enterprise_types}
            onChangeText={(text) => setEnterprise_types(text)}
/>
  </View>
 );
const renderScene = SceneMap({
  first: FirstRoute,
  second: SecondRoute,
});

  const [index, setIndex] = React.useState(0);
  const [routes] = React.useState([
    { key: 'first', title: 'First' },
    { key: 'second', title: 'Second' },
  ]);

  return (
    <TabView
      navigationState={{ index, routes }}
      renderScene={renderScene}
      onIndexChange={setIndex}
      initialLayout={{ width: layout.width }}
    />
  );
}
samakintunde commented 3 years ago

Currently having this issue as well. The only way that I've been able to prevent the dismissal is to set keyboardDismissMode: 'none' on the TabView. I tried setting it to 'on-drag' but it still kept dismissing the keyboard once the tab view re-renders.

<TabView
    keyboardDismissMode="none" // set keyboardDismissMode to 'none'
    navigationState={{index, routes}}
    onIndexChange={setTabIndex}
    initialLayout={{width: layout.width}}
/>
devWaleed commented 3 years ago

@samakintunde37 keyboardDismissMode="none" doesn't work for me. I am getting the same error.

nicolak commented 3 years ago

Same problem

samakintunde commented 3 years ago

@nathangabriel27 The issue with your component is the level you're declaring enterprise_types at. If the state isn't being shared between both tabs, you should move the state into the FirstRoute component, where you need it and you should be good.

samakintunde commented 3 years ago

@devWaleed and @nicolak, check above. Same situation?

nicolak commented 3 years ago
const FirstRoute = () => {
        const [myValue, setMyValue] = useState('');
        return (
            <View style={{ flex: 1, backgroundColor: '#ff4081' }} >
                <C.TextoInput 
                    value={myValue}
                    onChangeText={(t)=>setMyValue(t)}
                />
            </View>
        ) };

That way, the keyboard does not close. However, when switching to another tab, the State myValue is reset to zero.

What to do?

samakintunde commented 3 years ago
export default function TabViewExample() {
  // Keep state outside the components so it persists across tabs
  const [myValue, setMyValue] = useState('');

  const layout = useWindowDimensions();

  const [index, setIndex] = React.useState(0);
  const [routes] = React.useState([
    {key: 'first', title: 'First'},
    {key: 'second', title: 'Second'},
  ]);

  // Use a custom renderScene function instead
  const renderScene = ({route}) => {
    switch (route.key) {
      case 'first':
        return (
          <View style={{flex: 1, backgroundColor: '#ff4081', padding: 48}}>
            <TextInput
              style={{backgroundColor: '#fff'}}
              value={myValue}
              onChangeText={setMyValue}
            />
          </View>
        );
      case 'second':
        return <View style={{flex: 1, backgroundColor: '#ff4081'}} />;

      default:
        return <View />;
    }
  };

  return (
    <TabView
      navigationState={{index, routes}}
      renderScene={renderScene}
      onIndexChange={setIndex}
      initialLayout={{width: layout.width}}
    />
  );
}
nicolak commented 3 years ago

Wow. It worked out. Thanks!

samakintunde commented 3 years ago

You're welcome!👍

devWaleed commented 3 years ago

Here is my code. I am using class based components. On my Home component/screen I am returning few views and TabView is one of them. I have used tabsSceneMap as a custom sceneMap function already. All my tab tab screens are React.PureComponent. Still the issue persist for me.

// class Home extends React.Component

state = {
    currentTabIndex: 0,
};

tabRoutes = [
    {
        key: 'home',
        title: 'Home',
        icon: (color) => <Icon name="home" solid size={20} color={color} />,
    },
    {
        key: 'products',
        title: 'Products',
        icon: (color) => (
            <Icon name="shopping-bag" solid size={20} color={color} />
        ),
    },
    {
        key: 'consultant',
        title: 'Consultant',
        icon: (color) => (
            <Icon name="user-clock" solid size={20} color={color} />
        ),
    },
    {
        key: 'blogs',
        title: 'Blogs',
        icon: (color) => <Icon name="blogger-b" solid size={20} />,
    },
];

render() {
    return <View style={{ flex: 1 }}>
                    {this.tabViews()}
                </View>
}

tabsSceneMap({ route }) {
    switch (route.key) {
        case 'home':
            return <HomeTab
                navigation={this.props.navigation}
                changeTabCallback={this.changeTabCallback}
            />;
        case 'products':
            return <ProductsTab navigation={this.props.navigation} />;
        case 'consultant':
            return <Consultant />;
        case 'blogs':
            return <BlogsListing navigation={this.props.navigation} />;
    }
}

changeTabCallback = (tab) => {
    switch (tab) {
        case 'products':
            this.setState({
                currentTabIndex: 1,
            });
            this.props.setSearchBoolean(false);
            break;
        default:
            this.setState({
                currentTabIndex: 0,
            });
            break;
    }
};

tabViews() {
    return (
        <TabView
            lazy={true}
            swipeEnabled={false}
            renderTabBar={() => null}
            navigationState={{
                index: this.state.currentTabIndex,
                routes: this.tabRoutes,
            }}
            renderScene={this.tabsSceneMap}
            onIndexChange={(currentTabIndex) =>
                this.setState({ currentTabIndex })
            }
            initialLayout={{ width: windowWidth }}
        />
    );
}
moh3n9595 commented 3 years ago

Same issue

MaxToyberman commented 3 years ago

same for me

dariomalfatti-centropaghe commented 3 years ago

solution of @samakintunde37 doesn't solve my issue, downgrade to 2.16.0 to temporary solve and wait a bug fix or a solution

gamingumar commented 3 years ago

I am having this issue in iOS. Any text input in a tabview hides the keyboard and it doesn't open again. Downgraded to 2.16.0 for the fix.

github-actions[bot] commented 3 years ago

Hey! Thanks for opening the issue. Can you provide a minimal repro which demonstrates the issue? Posting a snippet of your code in the issue is useful, but it's not usually straightforward to run. A repro will help us debug the issue faster. Please try to keep the repro as small as possible and make sure that we can run it without additional setup.

The easiest way to provide a repro is on snack.expo.dev. If it's not possible to repro it on snack.expo.dev, then please provide the repro in a GitHub repository.

bayraak commented 3 years ago

I have the same issue too

babyrusa commented 3 years ago

I'm having the same issue on IOS (it works fine on Android) for version 3.1.1. Downgraded to 2.16.0 and it worked.

github-actions[bot] commented 3 years ago

Hello 👋, this issue has been open for more than a month without a repro or any activity. If the issue is still present in the latest version, please provide a repro or leave a comment within 7 days to keep it open, otherwise it will be closed automatically. If you found a solution or workaround for the issue, please comment here for others to find. If this issue is critical for you, please consider sending a pull request to fix it.

EduFrazao commented 2 years ago

The problem still persists. I also have to downgrade to version 2.16.0. When a tab is selected with a focused textinput (via autoFocus or some listener that calls focus via some ref) the focus works, but in a second the keyboard is dismissed and focus is lost. EDIT: Its hapenning in Android (8, 9, 10 and 11)

achalofficial commented 2 years ago

Same issue Still Persists... I changed the function calling from (e) => function_name(e) to function_name but.... As soon as i enter the value it triggers the onChangeText () and hence leads to hiding keyboard... Kindly resolve the error

emilianraduu commented 2 years ago

Happens to me as well

okwasniewski commented 2 years ago

Reopening this, since the issue still persists

fmendoza commented 2 years ago

Yep, same issue here.

lotusshinoaki commented 2 years ago

My app also has the same issue. I checked the call stack of TextInput.blur() and got the following stack trace.

01

This line in react-native-pager-view has keyboard close code that only works on Android (derivatively the TextInput loses focus). https://github.com/callstack/react-native-pager-view/blob/0c65e4dbd296861855a3e3f5b0bb9942f2dc9248/src/PagerView.tsx#L71

After removing this Keyboard.dismiss() the TextInput no longer loses focus in my app.

Update My workaround

  const [
    keyboardDismissMode,
    setKeyboardDismissMode,
  ] = React.useState<'none' | undefined>();
  const navigation = useNavigation();

  React.useEffect(() => {
    if (Platform.OS !== 'android') {
      return;
    }

    const unsubscribeOnFocus = navigation.addListener('focus', () => {
      setKeyboardDismissMode(undefined);
    });
    const unsubscribeOnBlur = navigation.addListener('blur', () => {
      setKeyboardDismissMode('none');
    });

    return () => {
      unsubscribeOnFocus();
      unsubscribeOnBlur();
    };
  }, [navigation]);

  return <TabView keyboardDismissMode={keyboardDismissMode} />;
AwesomeDracula commented 2 years ago

Adding animationEnabled={false} to solves the problem for me! <TabView animationEnabled={false} navigationState={state} renderScene={_renderScene} renderTabBar={_renderTabBar} onIndexChange={_handleIndexChange} tabBarPosition="bottom" />