No more lagging, no more bugs

React Native Uncontrolled Inputs

Unfortunately, there's a huge bug in React Native at the moment. When you use a controlled input, the most common and natural input in react-native, the typing lags if you do so too fast. The topic has been covered by important figures as Dan Abramov and Theo.gg but the good solution is not out there easily obtainable. In this post we'll cover the issue and a possible solution that has worked great for me.

The Issue

When a user types too quickly, the cursor can lag behind their input. You’ve likely experienced this before: the text becomes scrambled because the cursor moves out of sync with the end of the text, causing new characters to be inserted randomly in the middle. Here's an example in action.

Here I type pretty fast with my keyboard and you immediately see the lag

It is pretty anoying. I use a grocery shopping app here in Chile that has this issue and every time I open the search bar i have to be careful not to type too fast. It sucks.

If you want to know more about why this happens, you can check the PR to React Native docs that started the discussion by Dan Abramov.

The Solution

The solution is to use an uncontrolled input. But what is it? It is a component where instead of calling a function to set react state on every change, you pass a ref to the component. This way you can get the value out of the ref only when you need it. I don't manage the internals on how React does that but that's the gist. Do not set state on every change, pass a ref and then read from it only when needed. See here in my component.

function GithubRepoSearchBar() {
  const ref = useRef<TextInput>(null);
  const [query, setQuery] = useState(''); // We still have state! we just don't set it on every change
  const fetchRepos = async (searchText: string) => {
    // Code here to fetch repos...
    return response.data.items;
  };

  const { data, isFetching, refetch } = useQuery( // i use react-query but this doesn't really affect the example. Left it to showcase here is where we use the value in state.
    ['repoSearch', query],
    ({queryKey}) => fetchRepos(queryKey[1]),
    {
      enabled: !!query, // fetch when query is not empty 
    },
  );

  const onSubmitEditing = (
    event: NativeSyntheticEvent<TextInputSubmitEditingEventData>,
  ) => {
    if (event.nativeEvent.text.trim() !== '') {
      setQuery(event.nativeEvent.text); // only set state once submitted
    }
  };

  const clearSearch = () => {
    ref.current?.clear();
    setQuery('');
  };


  return (
    <View >
      <TextInput
        ref={ref}
        placeholder="Search for a Repo..."
        autoCapitalize="none"
        autoCorrect={false}
        autoFocus={false}
        onSubmitEditing={onSubmitEditing}
        enablesReturnKeyAutomatically
        returnKeyType="search"
      />
      {isFetching && query.trim() !== ''
        ? <ActivityIndicator size="small" color="#8B5CF6" />
        : null}
      <TouchableOpacity onPress={clearSearch}>
        <CircledCross color="fill-zinc-500" />
      </TouchableOpacity>
    </View>
  );
}

Then when typing fast you'll have a buttery smooth experience.

All looking well now with a uncontrolled input!

Hope it was useful.


By Matias Andrade

Building codereader and writing about it

@mag_devo on X
MySql repo on codereader

Meet Codereader, your place to investigate, learn and write down your thoughts.

Editing a C file on codereader

Meet Codereader, your place to investigate, learn and write down your thoughts.

Editing a C file on codereader

Your repos everywhere

Download Now!

download on play store