0

In useEffect with fetch(api), I have set [search]. This shows "loading..." every time I type something in the input box and I have to click on the box to type next word/number.

I'd like to know how I can avoid it so that I can keep typing without choosing the box every single letter I typed.

I have JSON data which has "login" as the username. It would be appreciated if I could get help.

App.js file:

import React, {useState, useEffect} from "react";
import './App.css';
import UserDetails from "./UserDetails"

function App() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [search, setSearch] = useState('');

  useEffect(() => {
    if(!search) {
      return;
    }
    setLoading(true);

    fetch(`https://api.github.com/search/users?q=${search}`)
      .then(res => res.json())
      .then(res => {
        console.log(res)
        setUsers(res.items);
        setLoading(false);
      })
      .catch(err => {
        console.log(err);
      });
  }, [search]); // every time search changes, we call the fn

  if(loading) {
    return <p>Loading...</p>
  }

  return(
    <div>
      <h1>Github user searcher</h1>
      <input
        type="text"
        placeholder="Search"
        onChange={e => setSearch(e.target.value)}
        value={search}
      />

      {users.map((user, name) => (  // {}you have ti return inthis fn, () using this return directly
        <UserDetails
          key={name}
          {...user}
        />
      ))}

    </div>
  )

};

export default App;

UserDetails.js file:

import React from "react"

function UserDetails(props) {
  return(
    <div>
      {props.login}
    </div>
  )
}

export default UserDetails
2
  • Its because your are setting loading true. So as input changes. It set true and the false. What do you want when user is typing? Commented Aug 26, 2020 at 11:50
  • yes @ShubhamVerma is right, every time you type something, search is modified, so your effect is called, and you setLoading(true), si it destroys all your input, userDetails etc, and mounts the p tag. Then after setLoading(false) a new input is mounted = you have lost the focus. Commented Aug 26, 2020 at 11:53

3 Answers 3

1

Simple solution would be to debounce setSearch, that way it would execute the search only after user stops typing (delayed execution)

import React, {useState, useEffect} from "react";
import './App.css';
import UserDetails from "./UserDetails"

function debounce(callback, wait) {
  let timeout
  return (...args) => {
    const context = this
    clearTimeout(timeout)
    timeout = setTimeout(() => callback.apply(context, args), wait)
  }
}

function App() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(false);
  const [search, setSearch] = useState('');
  // this will create a new function that will execute setSearch when it wasn't called for 200 ms
  const debouncedSetSearch = debounce((...args) => setSearch(...args), 200);

  useEffect(() => {
    if(!search) {
      return;
    }
    setLoading(true);

    fetch(`https://api.github.com/search/users?q=${search}`)
      .then(res => res.json())
      .then(res => {
        console.log(res)
        setUsers(res.items);
        setLoading(false);
      })
      .catch(err => {
        console.log(err);
      });
  }, [search]); // every time search changes, we call the fn

  if(loading) {
    return <p>Loading...</p>
  }

  return(
    <div>
      <h1>Github user searcher</h1>
      <input
        type="text"
        placeholder="Search"
        onChange={e => debouncedSetSearch(e.target.value)}
        value={search}
      />

      {users.map((user, name) => (  // {}you have ti return inthis fn, () using this return directly
        <UserDetails
          key={name}
          {...user}
        />
      ))}

    </div>
  )

};

export default App;
Sign up to request clarification or add additional context in comments.

Comments

0

As a solution, you might consider not to hide search input when searching. Something like:

import React, { useState, useEffect } from 'react'
import './App.css'
import UserDetails from './UserDetails'

function App() {
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(false)
  const [search, setSearch] = useState('')

  useEffect(() => {
    if (!search) {
      return
    }

    setLoading(true)

    const abortController = new AbortController()

    fetch(`https://api.github.com/search/users?q=${search}`, {
      signal: abortController.signal,
    })
      .then((res) => res.json())
      .then((res) => {
        console.log(res)
        setUsers(res.items)
        setLoading(false)
      })
      .catch((err) => {
        console.log(err)
      })

    // Abort previously created Fetch request
    // if search param has been changed in order
    // not to overload server with redundant requests
    return () => {
      abortController.abort()
    }
  }, [search]) // every time search changes, we call the fn

  return (
    <div>
      <h1>Github user searcher</h1>
      <input
        type="text"
        placeholder="Search"
        onChange={(e) => setSearch(e.target.value)}
        value={search}
      />

      {loading && <p>Loading...</p>}

      {!loading &&
        users.map((
          user,
          name // {}you have ti return inthis fn, () using this return directly
        ) => <UserDetails key={name} {...user} />)}
    </div>
  )
}

export default App

You can also consider setting up focus with the help of ref, but the flickering search box might provide an unpleasant user experience.

UPD

I updated the example snippet with aborting redundant requests.

1 Comment

Moreover I would debounce the effect, easy to do with a setTimeout and clearTimeout. After your edit : Interesting, the abort thing. axios has a similar mechanism cancelToken for those who would be interested.
0

I used setTimeout. It is worked too.

Look my Example

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.