简体   繁体   中英

React Cannot read property 'map' of undefined

In react, I can set a variable within a component, but I am having a hard time setting var data = {} within just a regular JavaScript file - list.js

I retrieve my data using Axios, but data.map is undefined, but the data is not undefined, as seen in the console. I am assuming that this is because data.map is being called before data has been retrieved from the Axios.get. The reason why I say this, is because when I do a console.log(data), I can see the data in the console:

import React from 'react';
import Axios from 'axios';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';

var data = {};

function componentDidMount() {
    Axios.get('http://localhost/api/get-coins/')
        .then(
            responseJson => {
                data = JSON.parse(JSON.stringify(responseJson.data));
                // console.log(data);
                return data;
            }
        )
        .catch(
            error => console.log(error)
        )
}

function SimpleTable(props) {
    const { classes } = props;
    data = componentDidMount();
    console.log(data);
    return (
        <Paper className={classes.root}>
            <Table className={classes.table}>
                <TableHead>
                    <TableRow>
                        <TableCell numeric>ID</TableCell>
                        <TableCell>Name</TableCell>
                        <TableCell numeric>Price</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                    {data.map(n => {
                        return (
                            <TableRow key={n.id}>
                                <TableCell component="th" scope="row">
                                    {n.name}
                                </TableCell>
                                <TableCell numeric>{n.price}</TableCell>
                            </TableRow>
                        );
                    })}
                </TableBody>
            </Table>
        </Paper>
    );
}

SimpleTable.propTypes = {
    classes: PropTypes.object.isRequired,
};

How can I retrieve the data first, followed by calling the SimpleTable prototype?

I have tried the following, with a setTimeout, but I receive an error that null was returned, but again, I see the data in the console:

function SimpleTable(props) {
    var data = {};

    const { classes } = props;
    data = Axios.get('http://local.sites/ng6crud/api/get-coins/')
        .then(
            responseJson => {
                data = JSON.parse(JSON.stringify(responseJson.data));
                console.log(data);
                setTimeout(function () {
                    return (
                        <Paper className={classes.root}>
                            <Table className={classes.table}>
                                <TableHead>
                                    <TableRow>
                                        <TableCell numeric>ID</TableCell>
                                        <TableCell>Name</TableCell>
                                        <TableCell numeric>Price</TableCell>
                                    </TableRow>
                                </TableHead>
                                <TableBody>
                                    {data.map(n => {
                                        return (
                                            <TableRow key={n.id}>
                                                <TableCell component="th" scope="row">
                                                    {n.name}
                                                </TableCell>
                                                <TableCell numeric>{n.price}</TableCell>
                                            </TableRow>
                                        );
                                    })}
                                </TableBody>
                            </Table>
                        </Paper>
                    );
                }, 1000);
            }
        )
        .catch(
            error => console.log(error)
        );
}

I feel like with React, I am fighting every step of the way. Please help and thanks in advance

Edit: Thanks to @guillaume answer. I was able to make it work. I want to point out that I was trying to do this without creating a class or component, but I guess there is no way to have the data in time before the render occurs.

import React from 'react';
import Axios from 'axios';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import Paper from '@material-ui/core/Paper';

class List extends React.Component {
    constructor(props) {
        super(props);
        this.state = {data: []}
    }

    componentDidMount() {
        Axios.get('http://localhost/api/get-coins/')
            .then(
                responseJson => {
                    this.setState({
                        data: JSON.parse(JSON.stringify(responseJson.data))
                    });
                    return this.state.data;
                }
            )
            .catch(
                error => console.log(error)
            )
    }

    render() {
        return (
            <Paper>
                <Table>
                    <TableHead>
                        <TableRow>
                            <TableCell numeric>ID</TableCell>
                            <TableCell>Name</TableCell>
                            <TableCell numeric>Price</TableCell>
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {this.state.data.map(n => {
                            return (
                                <TableRow key={n.id}>
                                    <TableCell component="th" scope="row">
                                        {n.id}
                                    </TableCell>
                                    <TableCell component="th" scope="row">
                                        {n.name}
                                    </TableCell>
                                    <TableCell numeric>{n.price}</TableCell>
                                </TableRow>
                            );
                        })}
                    </TableBody>
                </Table>
            </Paper>
        );
    }
}

export default List

You can't use React lifecycle methods inside a functional component unless you use react-pure-lifecycle .

Currently, your function componentDidMount is just that: a function you defined yourself. it does not behave the way React's componentDidMount method does.

You need to use a class component to use this method:

class SimpleTable extends React.Component {
    constructor(props) {
        super(props);
        this.state = {data: []}
    }

    componentDidMount() {
        Axios.get('http://localhost/api/get-coins/')
            .then(
                responseJson => {
                    this.setState({
                        data: JSON.parse(JSON.stringify(responseJson.data));

                    });

                    return data;
                }
            )
            .catch(
                error => console.log(error)
            )
    }

    render() {
        const { classes } = props;
        return (
            <Paper className={classes.root}>
                <Table className={classes.table}>
                    <TableHead>
                        <TableRow>
                            <TableCell numeric>ID</TableCell>
                            <TableCell>Name</TableCell>
                            <TableCell numeric>Price</TableCell>
                        </TableRow>
                    </TableHead>
                    <TableBody>
                        {data.map(n => {
                            return (
                                <TableRow key={n.id}>
                                    <TableCell component="th" scope="row">
                                        {n.name}
                                    </TableCell>
                                    <TableCell numeric>{n.price}</TableCell>
                                </TableRow>
                            );
                        })}
                    </TableBody>
                </Table>
            </Paper>
        );
    }
}

map is a method of Arrays but you initialise your data to an Object. Do var data = []; and your component will successfuly call .map on the empty array and render an empty table. Re-render your component when the data is ready and then the table will be filled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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