简体   繁体   English

使用自定义 React 钩子获取数据

[英]Fetch data with a custom React hook

I'm newbie in React but I'm developing an app which loads some data from the server when user open the app.我是 React 的新手,但我正在开发一个应用程序,当用户打开应用程序时,它会从服务器加载一些数据。 App.js render this AllEvents.js component: App.js 呈现这个AllEvents.js组件:

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. useEvents()EventServerHooks.js文件中的自定义钩子。 EventServerHooks is designed for incapsulating different ajax requests. EventServerHooks旨在封装不同的 ajax 请求。 (Like a helper file to make AllEvents.js cleaner) Here it is: (就像一个帮助文件,使 AllEvents.js 更干净)这里是:

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:这是第一个代码清单中的小组InfoMessages ,如果事件数组为空,它会显示消息“无结果”:

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正如您在代码中看到的,我将控制台日志放在useEvents()InfoMessages 中,因此当它显示时,它看起来像这样:如果显示事件则记录日志,以及应用程序本身

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?我错过了什么?

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.此外,您的自定义挂钩始终返回null ,这是错误的。 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:或者一个简单的变量(例如只有events数组,如果你不想要loading状态),然后像这样使用它:

const events = useEvents([])

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM