将API调用放在React函数组件外部

3

我对React比较陌生,我了解函数组件、类组件以及useState和useEffect等钩子的概念;因此,在处理以下React组件时遇到了麻烦,该组件进行了一个简单的JavaScript API调用,现在我想将其转换为“真正”的React API调用,并使用State和Hooks。

我的问题是:我想呈现API返回的员工对象,从员工名字和姓氏开始,然后是其他信息。

纯JavaScript对API的请求成功并返回所需的数据;因此,我不太确定根据定义设置状态应该是什么(0?false?还是其他什么?这取决于什么,我如何知道?)

这是代码:

import React, {useEffect, useState} from 'react';
import { Link } from "react-router-dom";
import {
    Container,
    Row,
    Col,
    Card,
    CardBody,
    Table,
    Button, Alert, Modal, ModalHeader, ModalBody
} from "reactstrap";


const ContactsList = (props) => {

        let request = new XMLHttpRequest()

        // Open a new connection, using the GET request on the URL endpoint
        request.open('GET', 'https://somenet.net/employee', true)

        request.onload = function () {
            // Begin accessing JSON data here
            let data = JSON.parse(this.response)

           data.forEach((employee) => {
                // Log each movie's title
                console.log(employee.id, employee.firstname, employee.lastname, employee.performance_index, employee.min_customer_distance, employee.customer_distance_radius, employee.webfleet_obj_id, employee.default_employee_working_schedule_id)
            })
        }
        // Send request
        request.send()

        let employees = [


            {
                id: 1, img: "Null", name: "David McHenry", designation: "UI/UX Designer", email: "david@skote.com", projects: "125",
                skills: [
                    { name: "Photoshop" },
                    { name: "illustrator" }
                ]
            }
        ]

       const users = [
            {
                id: 1, img: "Null", name: "David McHenry", designation: "UI/UX Designer", email: "david@skote.com", projects: "125",
                skills: [
                    { name: "Photoshop" },
                    { name: "illustrator" }
                ]
            },
            {
                id: 2, img: avatar2, name: "Frank Kirk", designation: "Frontend Developer", email: "frank@skote.com", projects: "132",
                skills: [
                    { name: "Html" },
                    { name: "Css" },
                    { name: "2 + more" },
                ]
            },
            {
                id: 3, img: avatar3, name: "Rafael Morales", designation: "Backend Developer", email: "Rafael@skote.com", projects: "1112",
                skills: [
                    { name: "Php" },
                    { name: "Java" },
                    { name: "Python" },
                ]
            },
            {
                id: 4, img: "Null", name: "Mark Ellison", designation: "Full Stack Developer", email: "mark@skote.com", projects: "121",
                skills: [
                    { name: "Ruby" },
                    { name: "Php" },
                    { name: "2 + more" },
                ]
            },
            {
                id: 5, img: avatar4, name: "Minnie Walter", designation: "Frontend Developer", email: "minnie@skote.com", projects: "145",
                skills: [
                    { name: "Html" },
                    { name: "Css" },
                    { name: "2 + more" },
                ]
            },
            {
                id: 6, img: avatar5, name: "Shirley Smith", designation: "UI/UX Designer", email: "shirley@skote.com", projects: "136",
                skills: [
                    { name: "Photoshop" },
                    { name: "UI/UX Designer" }
                ]
            },
            {
                id: 7, img: "Null", name: "John Santiago", designation: "Full Stack Developer", email: "john@skote.com", projects: "125",
                skills: [
                    { name: "Ruby" },
                    { name: "Php" },
                    { name: "2 + more" },
                ]
            },
            {
                id: 8, img: avatar7, name: "Colin Melton", designation: "Backend Developer", email: "colin@skote.com", projects: "136",
                skills: [
                    { name: "Php" },
                    { name: "Java" },
                    { name: "Python" },
                ]
            },
        ];

    const DefaultEvents = [{
        id: 1,
        title: 'Hey!',
        start: new Date().setDate(new Date().getDate() + 1),
        className: 'bg-warning text-white'
    },
        {
            id: 2,
            title: 'See John Deo',
            start: new Date(),
            end: new Date(),
            className: 'bg-success text-white'
        },
        {
            id: 3,
            title: 'Meet John Deo',
            start: new Date().setDate(new Date().getDate() + 8),
            className: 'bg-info text-white'
        },
        {
            id: 4,
            title: 'Buy a Theme',
            start: new Date().setDate(new Date().getDate() + 7),
            className: 'bg-primary text-white'
        }];

    const DefaultCategories = [
        {
            id: 1,
            title: 'New Theme Release',
            type: 'success'
        },
        {
            id: 2,
            title: 'My Event',
            type: 'info'
        },
        {
            id: 3,
            title: 'Meet Manager',
            type: 'warning'
        },
        {
            id: 4,
            title: 'Report Error',
            type: 'danger'
        },
    ];
    const event1= { id: 0, title: "", title_category: "", start: "", className: "", category: "", event_category: "" };
    const [calendarEvents, setCalendarEvents] = useState(DefaultEvents);
    const [categories, setCategories] = useState(DefaultCategories);
    const [modal, setModal] = useState(false);
    const [modal1, setModal1] = useState(false);
    const [modalcategory, setModalcategory] = useState(false);
    const [event, setEvent] = useState(event1);
    const [selectedDay, setSelectedDay] = useState(0);
    const title_category = false;

    const calendarComponentRef = React.createRef();
    

    useEffect(() => {
        new Draggable(document.getElementById("external-events"), {
            itemSelector: '.external-event',
        });
    });

    /**
     * Handling the modal state
     */
    function toggle() {
        setModal(!modal)
    }

    function toggle1() {
        setModal1(!modal1)
    }

    function togglecategory() {
        setModalcategory(!modalcategory)
    }

    /**
     * Handling date click on calendar
     */
    const handleDateClick = (arg) => {
        setSelectedDay(arg);
        toggle();
    }

    /**
     * Handling click on event on calendar
     */
    const handleEventClick = (arg) => {
        const eventNew = arg.event;

        const event_tmp = { id: eventNew.id, title: eventNew.title, title_category: eventNew.title_category, start: eventNew.start, className: eventNew.classNames, category: eventNew.classNames[0], event_category: eventNew.classNames[0] };

        setEvent(event_tmp);
        toggle1();
    }

    /**
     * Handling submit event on event form
     */
    const handleValidEventSubmit = (e, values) => {
        var newEvent = {};


        newEvent = {
            id: calendarEvents.length + 1,
            title: values['title'],
            start: selectedDay ? selectedDay.date : new Date(),
            className: values.category + ' text-white'
        };


        // save new event
        setCalendarEvents(calendarEvents.concat(newEvent));
        setSelectedDay(null);

        toggle();
    }

    const handleValidEventSubmitEvent = (e, values) => {
        var newEvent = {};
        newEvent = { id: event.id, title: values.title, classNames: values.category + ' text-white', start: event.start };
        //first, remove array item, which we want to edit
        let filteredArray = calendarEvents.filter(item => item.id + "" !== event.id + "");

        //then concat update item details
        let NewArray = filteredArray.concat(newEvent);

        //store to state
        setCalendarEvents(NewArray);
        setEvent(null);
        setSelectedDay(null);

        toggle1();
    }

    const handleValidEventSubmitcategory = (e, values) => {

        var newEvent = {};

        newEvent = {
            id: calendarEvents.length + 1,
            title: values['title_category'],
            type: values.event_category
        };
        // categories.concat(newEvent);
        setCategories(categories.concat(newEvent));

        togglecategory();
    }

    /**
     * On calendar drop event
     */
    const onDrop = (event) => {
        const draggedEl = event.draggedEl;

        var newEvent = {
            id: calendarEvents.length + 1,
            title: draggedEl.innerText,
            start: event.date,
            className: draggedEl.getAttribute('data-type') + ' text-white'
        };

        // save new event
        setCalendarEvents(calendarEvents.concat(newEvent));
    }


    return (
             <React.Fragment>
                <div className="page-content">
                    <Container fluid>

                        {/* Render Breadcrumbs */}
                        <Breadcrumbs title="Contacts" breadcrumbItem="Users List" />
                        <Card>
                            <CardBody>
                                <Row>
                                    <Col lg={3}>
                                        <Button color="primary" className="font-16 btn-block" onClick={() => togglecategory() }>
                                            <i className="mdi mdi-plus-circle-outline"></i> Create New Event
                                        </Button>

                                        <div id="external-events" className="mt-3">
                                            <p className="text-muted">Drag and drop your event or click in the calendar</p>

                                            {categories.map((category, i) => {
                                                return <Alert color={category.type}>{category.title} </Alert>
                                            })}
                                        </div>
                                        </Col>
                                    <Col className="col-lg-3">
                                        <div className="table-responsive">
                                            <Table className="table-centered table-nowrap table-hover">
                                                <thead className="thead-light">
                                                <tr>
                                                    <th scope="col" style={{ width: "70px" }}>#</th>
                                                    <th scope="col">Name</th>
                                                </tr>
                                                </thead>
                                                <tbody>
                                                {
                                                    employees.map((user, i) =>
                                                        <tr key={"_user_" + i} >
                                                            <td>
                                                                {
                                                                    user.img === "Null"
                                                                        ? <div className="avatar-xs">
                                                                                <span className="avatar-title rounded-circle">
                                                                                    {user.name.charAt(0)}
                                                                                </span>
                                                                        </div>
                                                                        : <div>
                                                                            <img className="rounded-circle avatar-xs" src={user.img} alt="" />
                                                                        </div>
                                                                }

                                                            </td>
                                                            <td>
                                                                <h5 className="font-size-14 mb-1"><Link to="#" className="text-dark">{user.name}</Link></h5>
                                                                <p className="text-muted mb-0">{user.designation}</p>
                                                            </td>
                                                        </tr>
                                                    )
                                                }
                                                </tbody>
                                            </Table>
                                        </div>

                                    </Col>
                                    <Col className="col-lg-6">
                                        {/* fullcalendar control */}
                                        <FullCalendar ref={calendarComponentRef} defaultView="dayGridMonth" plugins={[BootstrapTheme, dayGridPlugin, interactionPlugin]}
                                                      slotDuration={'00:15:00'}
                                                      minTime={'08:00:00'}
                                                      maxTime={'19:00:00'}
                                                      handleWindowResize={true}
                                                      themeSystem="bootstrap"
                                                      header={{
                                                          left: 'prev,next today',
                                                          center: 'title',
                                                          right: 'dayGridMonth,dayGridWeek,dayGridDay'
                                                      }}
                                                      events={calendarEvents}
                                                      editable={true}
                                                      droppable={true}
                                                      eventLimit={true}
                                                      selectable={true}
                                                      dateClick={handleDateClick}
                                                      eventClick={handleEventClick}
                                                      drop={onDrop}
                                                      id="calendar" />

                                        <button onClick={() => togglecategory() }
                                                className="btn btn-secondary float-right btn-lg waves-effect btn btn-secondary">
                                            Neuen Termin anlegen
                                        </button>
                                        {/* New event modal */}
                                        <Modal isOpen={modal} toggle={() => toggle()} className="">
                                            <ModalHeader toggle={() => toggle()} tag="h4">
                                                Add Event
                                            </ModalHeader>
                                            <ModalBody>
                                                <AvForm onValidSubmit={handleValidEventSubmit}>
                                                    <Row form>
                                                        <Col className="col-12">
                                                            <AvField name="title" label="Event Name" type="text" errorMessage="Invalid name" validate={{
                                                                required: { value: true }
                                                            }} value={event ? event.title : ''} />
                                                        </Col>
                                                        <Col className="col-12">
                                                            <AvField type="select" name="category" label="Select Category"
                                                                     value={event ? event.category : 'bg-primary'}>
                                                                <option value="bg-danger">Danger</option>
                                                                <option value="bg-success">Success</option>
                                                                <option value="bg-primary">Primary</option>
                                                                <option value="bg-info">Info</option>
                                                                <option value="bg-dark">Dark</option>
                                                                <option value="bg-warning">Warning</option>
                                                            </AvField>
                                                        </Col>
                                                    </Row>
                                                    <Row>
                                                        <Col>
                                                            <div className="text-right">
                                                                <button type="button" className="btn btn-light mr-2" onClick={() => toggle()}>Close</button>
                                                                <button type="submit" className="btn btn-success save-event">Save</button>
                                                            </div>
                                                        </Col>
                                                    </Row>
                                                </AvForm>
                                            </ModalBody>
                                        </Modal>

                                        {/* edit event modal */}
                                        <Modal isOpen={modal1} toggle={() => toggle1()} className="">
                                            <ModalHeader toggle={() => toggle1()} tag="h4">
                                                Edit Event
                                            </ModalHeader>
                                            <ModalBody>
                                                <AvForm onValidSubmit={handleValidEventSubmitEvent}>
                                                    <Row form>
                                                        <Col className="col-12">
                                                            <AvField name="title" label="Event Name" type="text" errorMessage="Invalid name" validate={{
                                                                required: { value: true }
                                                            }} value={event ? event.title : ''} />
                                                        </Col>
                                                        <Col className="col-12">
                                                            <AvField type="select" name="category" label="Select Category"
                                                                     value={event ? event.category : 'bg-primary'}>
                                                                <option value="bg-danger">Danger</option>
                                                                <option value="bg-success">Success</option>
                                                                <option value="bg-primary">Primary</option>
                                                                <option value="bg-info">Info</option>
                                                                <option value="bg-dark">Dark</option>
                                                                <option value="bg-warning">Warning</option>
                                                            </AvField>
                                                        </Col>
                                                    </Row>
                                                    <Row>
                                                        <Col>
                                                            <div className="text-right">
                                                                <button type="button" className="btn btn-light mr-2" onClick={() => toggle()}>Close</button>
                                                                <button type="submit" className="btn btn-success save-event">Save</button>
                                                            </div>
                                                        </Col>
                                                    </Row>
                                                </AvForm>
                                            </ModalBody>
                                        </Modal>

                                        <Modal isOpen={modalcategory} toggle={() => togglecategory()} className="">
                                            <ModalHeader toggle={() => togglecategory()} tag="h4">
                                                Add a category
                                            </ModalHeader>
                                            <ModalBody>
                                                <AvForm onValidSubmit={handleValidEventSubmitcategory}>
                                                    <Row form>
                                                        <Col className="col-12">
                                                            <AvField name="title_category" label="Category Name" type="text" errorMessage="Invalid name" validate={{
                                                                required: { value: true }
                                                            }} value={title_category ? event.title_category : ''} />
                                                        </Col>
                                                        <Col className="col-12">
                                                            <AvField type="select" name="event_category" label="Choose Category Color"
                                                                     value={event ? event.event_category : 'bg-primary'}>
                                                                <option value="bg-danger">Danger</option>
                                                                <option value="bg-success">Success</option>
                                                                <option value="bg-primary">Primary</option>
                                                                <option value="bg-info">Info</option>
                                                                <option value="bg-dark">Dark</option>
                                                                <option value="bg-warning">Warning</option>
                                                            </AvField>
                                                        </Col>
                                                    </Row>
                                                    <Row>
                                                        <Col>
                                                            <div className="text-right">
                                                                <button type="button" className="btn btn-light mr-2" onClick={() => togglecategory()}>Close</button>
                                                                <button type="submit" className="btn btn-success save-event">Save</button>
                                                            </div>
                                                        </Col>
                                                    </Row>
                                                </AvForm>
                                            </ModalBody>
                                        </Modal>
                                    </Col>
                                </Row>
                            </CardBody>
                        </Card>
                    </Container>
                </div>
            </React.Fragment>
          );
    }
        
export default ContactsList;

任何提示或帮助都将不胜感激 - 预先致谢!


3
如果您的问题只包含解决问题所必需的代码,那么回答您的问题会更容易些。 - Christian
3
这里有很多无关的代码。如果你只展示与反馈相关的代码部分,那么你可能会得到更好的反馈。例如,人们并不需要看到你的模态框实现,只需要关注如何正确实现数据获取即可。 - Patrick Roberts
1
请尝试访问 https://react-query.tanstack.com/docs/examples/simple。 - Ramesh
2
可能是不相关的话题 - 尝试专注并学习如何编写应用程序。在这个例子中,所有的东西都在一个单一的组件中,这可能不是很好。它们可以分成自己的组件。 - Ramesh
1个回答

10

有几件事情需要注意:

  • 不应该在函数本身中获取数据 - 这样会在每次组件渲染时触发一次获取。通常情况下,您希望在组件第一次渲染时开始获取:
useEffect(() => {
  fetchData().then(response => {
    const employees = JSON.parse(response)
    setEmployees(employees)
  })
}, [])

另请参阅此问题:ReactJS:如何调用useEffect hook仅一次以获取API数据

现在我们有了正确的获取数据方式,我们来到另一个问题 - 根据定义应该将状态设置为什么。换句话说,如何初始化数据。有人可能会告诉你可以是任何东西,但实际上,如果你正在使用数组(你正在遍历employees对象,因此我假设它是一个数组),最好用一个空数组进行初始化。这样类型就不会改变,下面的JSX也不需要额外的条件逻辑来处理不同类型的员工。

const [employees, setEmployees] = useState([])

我通常从useState调用开始,然后再调用useEffect。其实不确定如果你交换它们是否会出问题。所以总的代码应该是:


const fetchData = async () => {
    const res = await fetch('https://swapi.dev/api/people/')
    const json = await res.json()
    return json.result
}

const ContactsList = props => {
  const [employees, setEmployees] = useState([])

  useEffect(() => {
    fetchData().then(employees => {
      setEmployees(employees)
    })
  }, [])

  return (
    <div>
      {employees.map(employee => <div key={employee.id}>{employee.name}</div>)}
    </div>
  )
}

总的来说:

  • 你的组件相当大。React 的优美之处在于你可以将其划分为子组件。这不仅对你自己是一种良好的实践,而且更容易让其他 StackOverflow 用户理解你的问题。只保留必要的内容。

1
如果引用没有改变,setState()不一定会触发重新渲染。 - Patrick Roberts
我以为这就是React.memo()的作用...显然仍然在努力理解React的内部工作原理。 - Sventies
1
memo() 只决定了 props 的变化是否会触发重新渲染,而 setState() 是一种不使用 props 的不同机制。无论如何,你现在的答案是正确的,我已经通过点赞反映了这一点。 - Patrick Roberts
@Sventies:非常感谢您的解释!但是fetchData函数还缺失吧?它可能是什么样子的?我是这样实现的,但是出现了错误:async function fetchData() { const res = await fetch("https://some.net/employee'"); res .json() .then(res => setEmployees(res)) .catch(err => setErrors(err)); } - DWA
@Sventies:错误如下:未处理的拒绝(SyntaxError):JSON 中位置 0 处出现意外的 u 标记。 - DWA
1
@DieWebagenten 如果API实际上没有返回JSON,就会发生这种情况。如果它返回未定义,则JSON解析器可能会将未定义的“u”解释为第一个字符,而在JSON中应该是“null”。 - Sventies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接