简体   繁体   中英

React-Admin: Using "getList", I am getting "Error: cannot read property 'map' of undefined"

With react-admin , i'm trying to get the users list from API Restful Node server,
and I have this error:

Error: cannot read property 'map' of undefined

Here's the getUsers in server user.controller.js :

const getUsers = catchAsync(async (req, res) => {
  const users = await userService.getUsers(req.query);
  const data = users.map(user => user.transform());
  const total = users.length;

  res.type('json');
  res.set('Access-Control-Expose-Headers', 'Content-Range');
  res.set('Content-Range', `users 0-2/${total}`);
  res.set('X-Total-Count', total);
  response = '{ data: ' + JSON.stringify(data) + ', total: ' + total + ' }';
  res.send(response);
});

Here the data response received:

{
 data: [
 {"id":"5e6f5e3b4cf60a67701deeae","email":"admin@test.com","firstname":"Ad","lastname":"Min","role":"admin"},
 {"id":"5e6f5e3b4cf60a67701deeaf","email":"test@test.com","firstname":"Jhon","lastname":"Doe","role":"user"}
 ],
 total: 2 
}

In react-admin , getList in dataProvider.js :

export default {
  getList: (resource, params) => {
    console.log(params);
    const { field, order } = params.sort;
    const query = {
      ...fetchUtils.flattenObject(params.filter),
      sortBy: field
    };
    const url = `${apiUrl}/${resource}?${stringify(query)}`;
    return httpClient(url).then(({ headers }, json) => ({
      data: json,
      total: parseInt(
        headers
          .get("Content-Range")
          .split("/")
          .pop(),
        10
      )
    }));
  },

Update: UserList.tsx

import React from "react";
import {
  TextField,
  Datagrid,
  DateInput,
  Filter,
  List,
  EmailField,
  SearchInput
} from "react-admin";
import { useMediaQuery, Theme } from "@material-ui/core";
import SegmentInput from "./SegmentInput";
import MobileGrid from "./MobileGrid";

const UserFilter = (props: any) => (
  <Filter {...props}>
    <SearchInput source="q" alwaysOn />
    <DateInput source="createdAt" />
    <SegmentInput />
  </Filter>
);

const UserList = (props: any) => {
  const isXsmall = useMediaQuery<Theme>(theme => theme.breakpoints.down("xs"));
  return (
    <List
      {...props}
      filters={<UserFilter />}
      sort={{ field: "createdAt", order: "desc" }}
      perPage={25}
    >
      {isXsmall ? (
        <MobileGrid />
      ) : (
        <Datagrid optimized rowClick="edit">
          <TextField source="id" />
          <EmailField source="email" />
          <TextField source="firstname" />
          <TextField source="lastname" />
          <TextField source="role" />
          <DateInput source="createdAt" />
          <DateInput source="updatedAt" />
        </Datagrid>
      )}
    </List>
  );
};

export default UserList;

Here the documentation with somes examples for getList:
https://marmelab.com/react-admin/DataProviders.html#writing-your-own-data-provider

I don't understand, i need help please, what wrong?

Thanks & Regards Ludo

Here's a detailed explanation of what's happening with the map() in reactjs .

And this source is on point for resolving this in nodejs

In your case, let's take a closer look here:

// user.controller.js

const getUsers = catchAsync(async (req, res) => {
  // Actually, it might be wiser to first declare users
+ let users = [];

  // You await for users (querying) to be resolved or rejected, cool
- const users = await userService.getUsers(req.query);

  // And we then we assign "users" as before
+ users = await userService.getUsers(req.query);

  // But here, map() requires users to be defined before iteration.
  // So for this to work, we need the value of users to be resolved first, right?
- const data = users.map(user => user.transform());
  
  // Could you change the above line to this, and see?
  // This doesn't because we gave map(), a synchronous parameter (function)
- const data = Promise.all(users.map(user => user.transform()));
  
  // Ok, break this up for easier understanding
  // let's declare a userList, where we give map() an "async" parameter
  // At this point, userList will be an "Array of Promises"
+ const userList = users.map(async user => user.transform());
  
  // Now we resolve all the Promises, and we should have our clean data
+ const data = await Promise.all(userList);
});

With that change, we make sure that map() runs after our users is resolved.
Does that work for you? Let me know.

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