Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React-Admin is populating my <List> with same element

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

like image 333
Maifee Ul Asad Avatar asked Sep 11 '25 19:09

Maifee Ul Asad


2 Answers

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’).

like image 163
MwamiTovi Avatar answered Sep 14 '25 13:09

MwamiTovi


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

like image 31
WiXSL Avatar answered Sep 14 '25 14:09

WiXSL