使用自定义 React 钩子获取数据 [英] Fetch data with a custom React hook

查看:82
本文介绍了使用自定义 React 钩子获取数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 React 的新手,但我正在开发一个应用程序,当用户打开应用程序时,它会从服务器加载一些数据.App.js 呈现这个 AllEvents.js 组件:

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()EventServerHooks.js 文件中的自定义钩子.EventServerHooks 旨在封装不同的 ajax 请求.(就像一个帮助文件让 AllEvents.js 更干净)这里是:

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 }

这是第一个代码清单中的小组件 InfoMessages,如果事件数组为空,它会显示消息无结果":

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

所以我的问题是事件会定期加载,但在应用程序打开后不会定期加载.正如您在代码中看到的,我将控制台日志放在 useEvents()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?

推荐答案

不要将钩子传递给自定义钩子:自定义钩子应该与特定组件分离并可能重用.此外,您的自定义挂钩始终返回 null,这是错误的.但是您的代码很容易修复.

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]
}

在这个例子中,自定义钩子返回一个数组,因为我们需要两个值,但你也可以返回一个带有两个键/值对的对象.或者一个简单的变量(例如只有 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([])

这篇关于使用自定义 React 钩子获取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆