使用React Hooks在不同页面中获取请求

时间:2019-10-13 19:46:24

标签: reactjs react-hooks fetch-api

我正在使用React Hooks建立一个网站,并且我有两个不同的页面(Workshops.js和Shows.js)从同一API提取数据,只是使用不同的参数(?type = 0和?type = 1) )。 提取数据后,我便在映射结果(在那儿有一个可重用的组件会很好。请参阅下面代码中的注释)。当用户单击表演或工作室时,他将被重定向到同一页面。

现在,代码可以正常工作了。 有没有更优雅的方法来避免重复相同的代码? ...类似于Angular中的服务?

谢谢!

这是Workshop.js。

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom'
import api from '../../maps/Api' 

const Workshops = () => {
    const [ workshops, setWorkshop ] = useState([])
    const [ isLoading, setIsLoading ] = useState(false)
    const [ error, setError ] = useState(null)
    const GET_URL = api.get.workshops /* http://someapi/workshops?type=0 */

    useEffect(() => {
        setIsLoading(true)
        fetch(GET_URL, {headers: {
            "Accept": "application/json",
            "Access-Control-Allow-Origin": "*"
        }})
        .then(res => {
            return (res.ok) ?  res.json() : new Error("Mistake!")
        })
        .then(workshops => {
            if(workshops.upcoming) {
                setWorkshop(workshops.upcoming);
            }
            setIsLoading(false);
        })
        .catch(error => {
            setError(error)
        })
    }, [GET_URL])

    if ( error ){ return <p>{ error.message }</p> }
    if ( isLoading ){
        return <p>Loading workshops...</p>
    }

    return(

        <main>
            <div className='content'>
                <div className='contentCol'>
                    <ul id='workshopBox'>
                    {
                        workshops.map( (workshop, i) => (
                            <li> // FROM HERE...
                                <div
                                    className='workshop-active'>
                                    <h2>{ workshop.title }</h2>
                                    <p>{ workshop.description }</p>
                                    <p>{ workshop.place }</p>
                                    <p>{ (new Date(workshop.date).toLocaleDateString("it-IT", {
                                            weekday: 'long',
                                            year: 'numeric',
                                            month: 'long',
                                            day: 'numeric'
                                          }))}</p>
                                    <p>{ (new Date(workshop.date).toLocaleTimeString("it-IT", {
                                            hour: '2-digit',
                                            minute: '2-digit',
                                            hour12: true
                                          }))}</p>
                                    <p> Full price { workshop.price_full + ', 00'} &euro; </p>
                                    <p> Early bird price { workshop.price_earlybirds + ', 00'} &euro; </p>
                                    <p>
                                    <Link to={`/workshops/${ workshop.id}`}>
                                        <button>Details</button>
                                    </Link>
                                    </p>
                                    <br/>
                                </div>
                            </li> //..to HERE I WOULD LIKE TO USE A REUSABLE COMPONENT
                            ))
                    }
                    </ul>
                </div>
            </div>
        </main>
        )
    }

export default Workshops

这是Shows.js

import React, { useState, useEffect } from 'react';
//import { Link } from 'react-router-dom'
import api from '../maps/Api'

const Spettacoli = () => {
    const [ shows, setShows ] = useState([])
    const [ isLoading, setIsLoading ] = useState(false)
    const [ error, setError ] = useState(null)
    const GET_URL = api.get.shows /* http://someapi/workshops?type=1 */

    useEffect(() => {
        setIsLoading(true)
        fetch(GET_URL, {headers: {
            "Accept": "application/json",
            "Access-Control-Allow-Origin": "*"
        }})
        .then(res => {
            return (res.ok) ?  res.json() : new Error("Mistake!")
        })
        .then(shows => {
            setShows(shows)
            setIsLoading(false)
        })
        .catch(error => {
            setError(error)
        })
    }, [GET_URL])


    return(
        <main>
            <div className='content'>
                <div className='contentCol'>
                    /* SAME INTERFACE AS WORKSHOP */

                </div>
            </div>
        </main>
        )
}

export default Shows

1 个答案:

答案 0 :(得分:1)

因此您可以创建自己的custom hook

function useMyDataFetch(GET_URL) {
    const [ data, setData ] = useState([])
    const [ isLoading, setIsLoading ] = useState(true)
    const [ error, setError ] = useState(null)

    useEffect(() => {
        let hasBeenAborted = false; // added
        setIsLoading(true)
        fetch(GET_URL, {headers: {
            "Accept": "application/json",
            "Access-Control-Allow-Origin": "*"
        }})
        .then(res => {
            return (res.ok) ?  res.json() : new Error("Mistake!")
        })
        .then(data => {
            if (hasBeenAborted) return; // added
            if(data.upcoming) {
                setData(data.upcoming);
            }
            setIsLoading(false);
        })
        .catch(error => {
            if (hasBeenAborted) return; // added
            setIsLoading(false); // added
            setError(error)
        });
        return () => { hasBeenAborted = true; } // added
    }, [GET_URL]);

    return { data, error, isLoading };
}

,并在您的组件中使用它。

注意我已标记为// added的行。 hasBeenAborted允许我们对由于相同原因而出于任何原因而更新GET_URL的情况做出反应。 Cleanup in useEffect确实很重要,因此我们要避免比赛条件。

我们可以使用AbortController来代替hasBeenAborted标志,但是我们仍然会落入catch分支,并且需要附加的if来区分请求是否已被取消或实际上是失败了所以只是对我的品味。

对于您的组件,它们将使用像这样的钩子:

const Workshops = () => {
    const {isLoading, error, data: workshops} = useMyDataFetch(api.get.workshops);

    if ( error ){ return <p>{ error.message }</p> }
    if ( isLoading ){
        return <p>Loading workshops...</p>
    }

    return(
      // the same here
    );

}

export default Workshops