React-Admin: при использовании «getList» я получаю сообщение «Ошибка: невозможно прочитать свойство map of undefined» - PullRequest
1 голос
/ 17 марта 2020

С react-admin я пытаюсь получить список пользователей с сервера API Restful Node, и у меня есть эта ошибка:

Error: cannot read property 'map' of undefined

Вот getUsers на сервере 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);
});

Здесь данные response получены:

{
 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 
}

В react-admin, getList в 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
      )
    }));
  },

Обновление: 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;

Вот документация с некоторыми примерами для getList: https://marmelab.com/react-admin/DataProviders.html#writing -your-own-data-provider

Я не понимаю, мне нужна помощь, пожалуйста, что не так?

Спасибо & С уважением Ludo

1 Ответ

0 голосов
/ 18 марта 2020

Вот подробное объяснение того, что происходит с map() в reactjs.

И этот источник предназначен для разрешения этого в nodejs

В вашем случае, давайте более подробно рассмотрим здесь:

// 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);
});

С этим изменением мы гарантируем, что map() будет запущен после разрешения нашего users. Это работает для вас? Дайте мне знать.

...