4

codesandbox.io

I'm trying to create a nice admin dashboard with react-admin.
As using spring in my backend and this is how am sending data to react-admin:

@GetMapping("admin/user")
ResponseEntity<List<User>> getAll()
{
   System.out.println(new Date());;

   HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.set("Content-Range", "posts 0-"+userRepository.findAll().size() + "/" + userRepository.findAll().size());
        responseHeaders.set("Origin", "http://localhost:3001");

    return ResponseEntity.ok()
             .headers(responseHeaders)
             .body(userRepository.findAll());
}

First of all this is not working and secondly, this is not even close to correct solution.

Unfortunately, my client renders the last thing over and over again.
As you can see below, element with id 129 is rendered over and over again!

react-admin populating list with same element

At the frontend, within react.js:

// Within main/parent component
class App extends Component {
  render() {
    return (
      <Admin dataProvider={restProvider('http://localhost:8080/admin')}>
         <Resource name="user" list={UserList}/>
      </Admin>
    );
  }
};

// child component
const UserList = (props) => (
   <List {...props}>
      <Datagrid>
         <TextField source="userID" />
         <TextField source="username" />
         <TextField source="firstName" />
         <TextField source="middleName" />
         <TextField source="lastName" />
         <TextField source="profileImageURL" />
         <DateField source="joiningTime" />
         <EditButton basePath="/posts" />
       </Datagrid>
    </List>
);

I think I need a method that sort of configures every controller response with a Content-Range header.

Please note that my returned data works fine in postman:

react-admin populating list with same element

MwamiTovi
  • 2,425
  • 17
  • 25
Maifee Ul Asad
  • 3,992
  • 6
  • 38
  • 86

2 Answers2

4

The never-ending challenge of consuming data from an API

Whatever is said in the answer above is true!
Based on the asked question, how do you provide an id field for <Datagrid> to iterate over your data and render each row/element uniquely?

Note that the React-Admin dataProvider often returns a response with a json Object {count: <Number>, results: <Array>}. And the results array is often mapped onto the data key e.g. data: json.results

Here's what helped me while I used react-admin with a python/django backend.
I believe in most cases, regardless of the backend, it's crucial to understand how react-admin consumes data (in this case a list). Two Solutions are possible:

  1. If you have control over your backend (API), just like here.
    Then kindly update/refactor your unique identifier (in this case, userID) to be mapped/returned as just a plain id field. This will make it easier for any client consuming your data using react-admin or react.js generally.

  2. Ok, so you have no control over that backend (API)!
    Then you'll have to use this line //data: json.map(record => ({"id": record.userID, ...record})).

Not too fast with solution two!
There lies the commonest pitfall when working with APIs.
While we fetch data, our client initially (the array/list, in this case json.results) is undefined.
So javascript will complain that it cannot read property 'map' of undefined.

How do I solve such a challenge of undefined?

Either directly initiate your data as an array or instruct the map() to only run if your data is not undefined (by including such a check, typeof json !== ‘undefined’).

MwamiTovi
  • 2,425
  • 17
  • 25
2

Try it here:

https://codesandbox.io/s/react-admin-sample-l692r

Each record must have a field named id which in your case represents your userID field.

Use users for the resource name instead of user.

If you cannot change that server side, you can do it in javascript.

The following sample code should work. Add it to AdminPanel.js file in your setup:

With your backend, uncomment the following lines:

//data: json.map(record => ({"id": record.userID, ...record})),

//total: parseInt(headers.get('content-range').split('/').pop(), 10),

And change the following line:

const apiUrl = "https://jsonplaceholder.typicode.com";

to

const apiUrl = "http://localhost:8080/admin";

import React from "react";
import { Admin, Resource, fetchUtils } from "react-admin";
import restProvider from "ra-data-simple-rest";
import { UserList, LoginCredentialList } from "./posts";
import { stringify } from "query-string";

const apiUrl = "https://jsonplaceholder.typicode.com";
const httpClient = fetchUtils.fetchJson;

const myDataProvider = {
    ...restProvider,
    getList: (resource, params) => {
        const { page, perPage } = params.pagination;
        const { field, order } = params.sort;
        const query = {
            sort: JSON.stringify([field, order]),
            range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
            filter: JSON.stringify(params.filter)
        };
        const url = `${apiUrl}/${resource}?${stringify(query)}`;

        return httpClient(url).then(({ headers, json }) => ({
            data: json,
            //data: json.map(record => ({"id": record.userID, ...record})),
            total: 10, //temporary hardcoded to avoid headers error.
            //total: parseInt(headers.get('content-range').split('/').pop(), 10),
        }));
    }
};

class AdminPanel extends React.Component {
    render() {
        return (
          <div>
              <Admin dataProvider={myDataProvider}>
                  <Resource name="users" list={UserList} />
                  <Resource name="loginCredential" list={LoginCredentialList} />
              </Admin>
          </div>
        );
    }
}

export default AdminPanel;

//                  <Resource name="posts" list={UserList} edit={PostEdit} create={PostCreate}/>

WiXSL
  • 689
  • 5
  • 16
  • it gives me [map undefined](https://i.stack.imgur.com/PzzOp.png) error, so i changed `data.map` to `json.map` it solved that problem, but gave me another new error : [has of undefined](https://i.stack.imgur.com/mSmds.png), you can see it is returning data correctly now : *data : Array(18)* – Maifee Ul Asad Jan 27 '20 at 14:50
  • Sorry about that. I didn't test it. The `has` error is due to the fact that the `headers` variable is not returned inside the Promise, since `ra-data-simple-rest` tries to use header's `has` method and `headers` parameter is `undefined`. I've corrected the example to include it. – WiXSL Jan 27 '20 at 16:53
  • I can't test code right now. In which line did you get that error? – WiXSL Jan 27 '20 at 17:38
  • Ok, you have to pass `data` attribute, I change the example again. Sorry. – WiXSL Jan 27 '20 at 18:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206739/discussion-between-wixsl-and-maifee-ul-asad). – WiXSL Jan 27 '20 at 18:40
  • in my snap, you can easily see that, i'm using data... if you really actually seeing the context i'm providing ... i have even marked that, in a red box... – Maifee Ul Asad Jan 27 '20 at 18:46
  • I've seen your snapshot. The error you get in `idsReducer` in line 23 is because `data` attribute is not getting found. ```js return action.payload.data.map(f... ``` – WiXSL Jan 27 '20 at 18:58
  • Look i'm just trying to help you – WiXSL Jan 27 '20 at 19:07
  • This is the correct return statement, like in the example. I don't know why is not working in your case. ```js return { json: json.map(record => ({"id": record.userID, ...record})), headers }; ``` – WiXSL Jan 27 '20 at 19:45
  • actually i'm little tensed that's why i may have misbehaved, here is the [sandbox](https://codesandbox.io/s/react-admin-sample-225lk?fontsize=14&hidenavigation=1&theme=dark) and here is [dummy json provider](https://jsonplaceholder.typicode.com/todos) – Maifee Ul Asad Jan 27 '20 at 20:07
  • if you can solve for this one, you have correct answer anyway, right ?? – Maifee Ul Asad Jan 27 '20 at 20:31
  • 1
    @MaifeeUlAsad, I set a up another example that should work. Please check it out. – WiXSL Jan 27 '20 at 21:13
  • See answer below, https://stackoverflow.com/a/60550603/10849438, might it help? – MwamiTovi Mar 05 '20 at 17:06