Fetch data with a custom React hook

拜拜、爱过 提交于 2021-02-09 07:29:11

问题


I'm newbie in React but I'm developing an app which loads some data from the server when user open the app. App.js render this AllEvents.js component:

const AllEvents = function ({ id, go, fetchedUser }) {
    const [popout, setPopout] = useState(<ScreenSpinner className="preloader" size="large" />)
    const [events, setEvents] = useState([])
    const [searchQuery, setSearchQuery] = useState('')
    const [pageNumber, setPageNumber] = useState(1)

    useEvents(setEvents, setPopout) // get events on the main page

    useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber)

    // for ajax pagination
    const handleSearch = (searchQuery) => {
        setSearchQuery(searchQuery)
        setPageNumber(1)
    }

    return(
        <Panel id={id}>
            <PanelHeader>Events around you</PanelHeader>
            <FixedLayout vertical="top">
                <Search onChange={handleSearch} />
            </FixedLayout>
            {popout}
            {
                <List id="event-list">
                    {
                        events.length > 0
                    ?
                        events.map((event, i) => <EventListItem key={event.id} id={event.id} title={event.title} />)
                    :
                        <InfoMessages type="no-events" />
                    }
                </List>
            }
        </Panel>
    )
}

export default AllEvents

useEvents() is a custom hook in EventServerHooks.js file. EventServerHooks is designed for incapsulating different ajax requests. (Like a helper file to make AllEvents.js cleaner) Here it is:

function useEvents(setEvents, setPopout) {
    useEffect(() => {
        axios.get("https://server.ru/events")
            .then(
                (response) => {
                    console.log(response)
                    console.log(new Date())
                    setEvents(response.data.data)
                    setPopout(null)
                },
                (error) => {
                    console.log('Error while getting events: ' + error)
                }
            )
    }, [])

    return null
}

function useSearchedEvents(setEvents, setPopout, searchQuery, pageNumber) {
    useEffect(() => {
        setPopout(<ScreenSpinner className="preloader" size="large" />)
        let cancel
        axios({
            method: 'GET',
            url: "https://server.ru/events",
            params: {q: searchQuery, page: pageNumber},
            cancelToken: new axios.CancelToken(c => cancel = c)
        }).then(
            (response) => {
                setEvents(response.data)
                setPopout(null)
            },
            (error) => {
                console.log('Error while getting events: ' + error)
            }
        ).catch(
            e => {
                if (axios.isCancel(e)) return
            }
        )

        return () => cancel()
    }, [searchQuery, pageNumber])

    return null
}

export { useEvents, useSearchedEvents }

And here is the small component InfoMessages from the first code listing, which display message "No results" if events array is empty:

const InfoMessages = props => {
    switch (props.type) {
        case 'no-events':
            {console.log(new Date())}
            return <Div className="no-events">No results :(</Div>
        default:
            return ''
    }
}

export default InfoMessages

So my problem is that events periodically loads and periodically don't after app opened. As you can see in the code I put console log in useEvents() and in InfoMessages so when it's displayed it looks like this: logs if events are displayed, and the app itself

And if it's not displayed it looks like this: logs if events are not displayed, and the app itself

I must note that data from the server is loaded perfectly in both cases, so I have totally no idea why it behaves differently with the same code. What am I missing?


回答1:


Do not pass a hook to a custom hook: custom hooks are supposed to be decoupled from a specific component and possibly reused. In addition, your custom hooks return always null and that's wrong. But your code is pretty easy to fix.

In your main component you can fetch data with a custom hook and also get the loading state like this, for example:

function Events () {
  const [events, loadingEvents] = useEvents([])

  return loadingEvents ? <EventsSpinner /> : <div>{events.map(e => <Event key={e.id} title={e.title} />}</div>
}

In your custom hook you should return the internal state. For example:

function useEvents(initialState) {
  const [events, setEvents] = useState(initialState)
  const [loading, setLoading] = useState(true)

  useEffect(function() {
    axios.get("https://server.ru/events")
            .then(
                (res) => {
                    setEvents(res.data)
                    setLoading(false)
                }
            )
  }, [])

  return [events, loading]
}

In this example, the custom hook returns an array because we need two values, but you could also return an object with two key/value pairs. Or a simple variable (for example only the events array, if you didn't want the loading state), then use it like this:

const events = useEvents([])


来源:https://stackoverflow.com/questions/60578865/fetch-data-with-a-custom-react-hook

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!