skip to Main Content

In my home page, I have to load some posts and songs from the database. Currently, I initially set a loading state variable to true, and then inside a useEffect, perform a get request for the posts and songs, store them in state, and once they have been fetched and stored, then set loading to false. I would simply display a loading spinner if loading was true, else I would display and loop over posts and songs

However, I want to display the posts and songs as soon as they are available, therefore if the posts are loaded faster than the songs, I want to display the posts and then a loading spinner below the displayed posts, indicating the songs are still being loaded.

My current solution to this is to have two loading state variables for posts and songs, such as loadingPosts and loadingSongs, and then have the following conditions to check if they are loading:

loadingPosts && <Spinner /> : <div>  Map over and display posts </div>
loadingSongs && <Spinner /> : <div>  Map over and display songs</div>

However, I would like a cleaner and more concise method to handle this. After researching, I discovered react-query to potentially help with this issue. Could react-query be used to simplify this code? Also, are there any tradtional react methods to simplify this? Thanks.

2

Answers


  1. Also, are there any traditional react methods to simplify this?

    Well instead of using a load state, you could check if the state (array) is empty, and if they’re both empty, show the loading state.

    You could do this inline, but I personally prefer this way since it’s more readable.


    const { useState } = React;
    
    const Example = () => {
    
        const [posts, setPosts] = useState([]);
        const [songs, setSongs] = useState([]);
      
        const postContent = (posts.length)
          ? posts.map(post => <p>{post}</p>)
          : null;
          
        const songContent = (songs.length)
          ? songs.map(song => <p>{song}</p>)
          : null;
        
        return (
            <div>
                <h1>{'Example'}</h1>
                <button onClick={() => setPosts(p => [ ...p, 'post #' + (p.length + 1) ])}>Add post</button>
                <button onClick={() => setSongs(s => [ ...s, 'song #' + (s.length + 1) ])}>Add song</button>
                <p>
                    {postContent}
                    {songContent}
                </p>
                {(!postContent && !songContent) && (
                    <p>
                        <em>Loading!</em><br />
                        <sub>Press one of the 'add' buttons to mimic the API call</sub>
                    </p>
                )}
            </div>
        )
    }
    ReactDOM.render(<Example />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>
    Login or Signup to reply.
  2. You can create a custom hook for fetching the data. It would be cleaner and reusable also. You can add error handling also.

    const useFetcher = (url) => {
       const [isFetching, setIsFetching] = useState(false)
       const [data,setData] = useState(null)
       const [isError, setIsError] = useState(false)
       
       useEffect(() => {
          setIsFetching(true)
          fetch(url)
             .then(response => response.json())
             .then(data => {
                 setIsFetching(false)
                 setData(data)
              })
             .catch(error => {
                 setIsFetching(false)
                 setIsError(false)
             });
       }, [url])
    
       return {
          isFetching, isError, data
       }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search