如何使用来自 API 响应的 JSON 渲染 material-table?

How to render a material-table using a JSON that comes from a API response?

我是 ReactJS 的新手,这是我创建的第一个页面,但我在设置变量方面遇到了一些问题。

我需要的是用来自 const response = await api.get('/users') 的值填充变量 table.data 并在 table 时用这个值渲染页面加载。

我有以下代码:

import React, { useState, useEffect } from 'react';
import { Fade } from "@material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '@material-ui/core/styles';

import api from '../../services/api.js';

const useStyles = makeStyles(theme => ({
    root: {
        flexGrow: 1,
        width: '70%',
        margin: 'auto',
        marginTop: 20,
        boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
    }
}));

function User(props) {
    const classes = useStyles();
    const [checked, setChecked] = useState(false);

    let table = {
        data: [
            { name: "Patrick Mahomes", sector: "Quaterback", email: "patrick@nfl.com", tel: "1234" },
            { name: "Tom Brady", sector: "Quaterback", email: "tom@nfl.com", tel: "5678" },
            { name: "Julio Jones", sector: "Wide Receiver", email: "julio@nfl.com", tel: "9876" }
        ]
    }

    let config = {
        columns: [
            { title: 'Name', field: 'name' },
            { title: 'Sector', field: 'sector' },
            { title: 'E-mail', field: 'email'},
            { title: 'Tel', field: 'tel'}
        ],
        actions: [
            { icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
            { icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
            { icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
            { icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
            { icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
        ],
        options: {
            headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
            actionsColumnIndex: -1,
            exportButton: true,
            paging: true,
            pageSize: 10,
            pageSizeOptions: [],
            paginationType: 'normal'
        },
        localization: {
            body: { 
                emptyDataSourceMessage: 'No data' 
            },
            toolbar: { 
                searchTooltip: 'Search',
                searchPlaceholder: 'Search',
                exportTitle: 'Export'
            },
            pagination: {  
                labelRowsSelect: 'Lines',
                labelDisplayedRows: '{from} to {to} for {count} itens',
                firstTooltip: 'First',
                previousTooltip: 'Previous',
                nextTooltip: 'Next',
                lastTooltip: 'Last'
            },
            header: {
                actions: 'Actions'
            }
        }
    }

    useEffect(() => {
        setChecked(prev => !prev);

        async function loadUsers() {
            const response = await api.get('/users');
            table.data = response.data;
        }

        loadUsers();
    }, [])


    return (
        <>
            <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
            <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />

            <Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
                <div className={classes.root}>
                    <MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={table.data} actions={config.actions}></MaterialTable>
                </div>
            </Fade>
        </>
    );
}

export default User;

前面的示例将显示我固定在变量 table.data 上的 3 个用户,其中包含 4 列(姓名、部门、​​电子邮件、电话)。

在函数式组件中,每一次渲染都是一次新的函数调用。因此,您在组件内声明并销毁并重新创建的任何变量。这意味着 table 会在每次渲染时设置回您的初始值。即使您的 useEffect 在第一次渲染后设置正确,它也只会在下一次渲染时重置。

这就是状态的用途:跟踪渲染之间的变量。将您的 let table 替换为新的状态挂钩。

const [table, setTable] = useState({
  data: [
    { name: "Patrick Mahomes", sector: "Quaterback", email: "patrick@nfl.com", tel: "1234" },
    { name: "Tom Brady", sector: "Quaterback", email: "tom@nfl.com", tel: "5678" },
    { name: "Julio Jones", sector: "Wide Receiver", email: "julio@nfl.com", tel: "9876" }
  ]
});

然后像这样使用它:

useEffect(() => {
  setChecked(prev => !prev);

  async function loadUsers() {
    const response = await api.get('/users');
     setTable(prev => ({...prev, data: response.data});
  }

  loadUsers();
}, [])

因为 table.data 不是状态变量,它会在每次组件渲染时按照最初声明的方式重新生成,这意味着当它作为组件的 prop 到达时它总是相同的值(当您在 useEffect 中更改 table.data 的值时,为时已晚)。您需要将 table.data 更改为状态变量,然后在您的 useEffect 挂钩中,您可以将 table.data 的值更新为 response.data 的值。这将导致组件重新呈现但具有更新的值。

下面是您可以如何执行此操作的示例:

import React, { useState, useEffect } from 'react';
import { Fade } from "@material-ui/core";
import MaterialTable from 'material-table';
import { makeStyles } from '@material-ui/core/styles';

import api from '../../services/api.js';

const useStyles = makeStyles(theme => ({
    root: {
        flexGrow: 1,
        width: '70%',
        margin: 'auto',
        marginTop: 20,
        boxShadow: '0px 0px 8px 0px rgba(0,0,0,0.4)'
    }
}));

function User(props) {
    const classes = useStyles();
    const [checked, setChecked] = useState(false);
    const [tableData, setTableData] = useState([]);


    let config = {
        columns: [
            { title: 'Name', field: 'name' },
            { title: 'Sector', field: 'sector' },
            { title: 'E-mail', field: 'email'},
            { title: 'Tel', field: 'tel'}
        ],
        actions: [
            { icon: 'create', tooltip: 'Edit', onClick: (rowData) => alert('Edit')},
            { icon: 'lock', tooltip: 'Block', onClick: (rowData) => alert('Block')},
            { icon: 'delete', tooltip: 'Delete', onClick: (rowData) => alert('Delete')},
            { icon: 'visibility', tooltip: 'Access', onClick: (rowData) => alert('Access')},
            { icon: "add_box", tooltip: "Add", position: "toolbar", onClick: () => { alert('Add') } }
        ],
        options: {
            headerStyle: { color: 'rgba(0, 0, 0, 0.54)' },
            actionsColumnIndex: -1,
            exportButton: true,
            paging: true,
            pageSize: 10,
            pageSizeOptions: [],
            paginationType: 'normal'
        },
        localization: {
            body: { 
                emptyDataSourceMessage: 'No data' 
            },
            toolbar: { 
                searchTooltip: 'Search',
                searchPlaceholder: 'Search',
                exportTitle: 'Export'
            },
            pagination: {  
                labelRowsSelect: 'Lines',
                labelDisplayedRows: '{from} to {to} for {count} itens',
                firstTooltip: 'First',
                previousTooltip: 'Previous',
                nextTooltip: 'Next',
                lastTooltip: 'Last'
            },
            header: {
                actions: 'Actions'
            }
        }
    }

    useEffect(() => {
        setChecked(prev => !prev);

        async function loadUsers() {
            const response = await api.get('/users');
            setTableData(response.data);
        }

        loadUsers();
    }, [])


    return (
        <>
            <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
            <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />

            <Fade in={checked} style={{ transitionDelay: checked ? '300ms' : '0ms' }}>
                <div className={classes.root}>
                    <MaterialTable editable={config.editable} options={config.options} localization={config.localization} title="Usuários" columns={config.columns} data={tableData} actions={config.actions}></MaterialTable>
                </div>
            </Fade>
        </>
    );
}

export default User;