Π Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° Π½Π° React: авторизация ΠΈ стайлинг

Π’ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΌ постС ΠΌΡ‹ Π½Π°Ρ‡Π°Π»ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ Π»ΠΈΡ‡Π½ΠΎΠ³ΠΎ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚Π° Π½Π° React ΠΈ ΡƒΠ·Π½Π°Π»ΠΈ ΠΊΠ°ΠΊ этот процСсс ΠΌΠΎΠΆΠ½ΠΎ ΡƒΡΠΊΠΎΡ€ΠΈΡ‚ΡŒ ΠΏΡ€ΠΈ ΠΏΠΎΠΌΠΎΡ‰ΠΈ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠ° React Admin. Π‘Ρ‹Π»ΠΈ рассмотрСны ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠ΅ ΠΌΠΎΠΌΠ΅Π½Ρ‚Ρ‹: установка ΠΈ пСрвичная настройка, Π²Ρ‹Π²ΠΎΠ΄ пСрСчня Π΄Π°Π½Π½Ρ‹Ρ… ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π½Ρ‹Ρ… ΠΏΠΎ API, Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π»ΠΈ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ записСй. ΠŸΡƒΡΡ‚ΡŒ ΠΏΠΎΠΊΠ° ΠΈ Π½Π° основС Ρ„ΠΈΠΊΡ‚ΠΈΠ²Π½ΠΎΠ³ΠΎ API, Π½ΠΎ ΠΌΡ‹ ΡƒΠΆΠ΅ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Π»ΠΈΡΡŒ ΠΊΠ°ΠΊ происходит процСсс Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ.

Π›ΠΈΡ‡Π½Ρ‹ΠΉ ΠΊΠ°Π±ΠΈΠ½Π΅Ρ‚ Π½Π΅ Π±Ρ‹Π» Π±Ρ‹ Π»ΠΈΡ‡Π½Ρ‹ΠΌ, Ссли Π±Ρ‹ Π½Π΅ Π΄Π°Π²Π°Π» Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡ΠΈΡ‚ΡŒ доступ ΠΊ Π΄Π°Π½Π½Ρ‹ΠΌ ΠΏΠΎ Π»ΠΎΠ³ΠΈΠ½Ρƒ ΠΈ ΠΏΠ°Ρ€ΠΎΠ»ΡŽ. Π’ этой ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΊΠ°ΠΊ Ρ€Π°Π· ΠΈ займСмся Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ процСсса Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ. Π’Π°ΠΊ ΠΆΠ΅ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ коснСмся вопроса стилизации нашСй административной ΠΏΠ°Π½Π΅Π»ΠΈ Π½Π° React.

АутСнтификация

react-admin позволяСт достаточно Π³ΠΈΠ±ΠΊΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Ρ‹Π²Π°Ρ‚ΡŒ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡŽ. JSONPlaceholder Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ, поэтому ΠΌΡ‹ собираСмся ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Ρ„ΠΈΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΉ процСсс Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ Π»ΡŽΠ±Ρ‹Π΅ значСния Π² качСствС ΠΈΠΌΠ΅Π½ΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ ΠΈ пароля ΠΈ ΡΠΎΡ…Ρ€Π°Π½ΡΡ‚ΡŒ эти значСния Π² localStorage. Π’ ΠΏΠ°ΠΏΠΊΠ΅ src создайтС Ρ„Π°ΠΉΠ» с ΠΈΠΌΠ΅Π½Π΅ΠΌ authProvider.js:

// in src/authProvider.js
export default {
    // called when the user attempts to log in
    login: ({ username }) => {
        localStorage.setItem('username', username);
        // accept all username/password combinations
        return Promise.resolve();
    },
    // called when the user clicks on the logout button
    logout: () => {
        localStorage.removeItem('username');
        return Promise.resolve();
    },
    // called when the API returns an error
    checkError: ({ status }) => {
        if (status === 401 || status === 403) {
            localStorage.removeItem('username');
            return Promise.reject();
        }
        return Promise.resolve();
    },
    // called when the user navigates to a new location, to check for authentication
    checkAuth: () => {
        return localStorage.getItem('username')
            ? Promise.resolve()
            : Promise.reject();
    },
    // called when the user navigates to a new location, to check for permissions / roles
    getPermissions: () => Promise.resolve(),
};

AuthProvider Π΄ΠΎΠ»ΠΆΠ΅Π½ ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ 5 ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ², ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ Promise. ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ Π²Ρ‹Π·ΠΎΠ²Ρ‹ authProvider ΡΠ²Π»ΡΡŽΡ‚ΡΡ асинхронными, Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ c Π»Π΅Π³ΠΊΠΎΡΡ‚ΡŒΡŽ Π·Π°ΠΏΡ€ΠΎΡΠΈΡ‚ΡŒ сСрвСр Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ.

Π§Ρ‚ΠΎΠ±Ρ‹ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ эту ΡΡ‚Ρ€Π°Ρ‚Π΅Π³ΠΈΡŽ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΈ, ΠΏΠ΅Ρ€Π΅Π΄Π°ΠΉΡ‚Π΅ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Ρƒ Π² качСствС свойства authProvider Π² ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π΅ <Admin>:

<Admin dashboard={Dashboard} authProvider={authProvider} dataProvider={dataProvider}>
// ...
</Admin>

ПослС ΠΏΠ΅Ρ€Π΅Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ прилоТСния, вывСдСтся Ρ„ΠΎΡ€ΠΌΠ° Π²Ρ…ΠΎΠ΄Π° Π² систСму, которая Π½Π° Π΄Π°Π½Π½Ρ‹ΠΉ ΠΌΠΎΠΌΠ΅Π½Ρ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Π»ΡŽΠ±Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅:

НСмного ΠΎ Ρ‚ΠΈΠΏΠ°Ρ… ΠΏΠΎΠ»Π΅ΠΉ

Π’ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰Π΅ΠΉ ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ ΠΊΠΎΡΠ½ΡƒΠ»ΠΈΡΡŒ вопроса Ρ‚ΠΈΠΏΠΎΠ² ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠΎΠ³Π΄Π° создавали ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠ΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π½Π° основС Π΄Ρ‹Π½Π½Ρ‹Ρ…, Π³Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌΡ‹Ρ… гСссСром. React-admin Π΄Π°Π΅Ρ‚ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·Π²ΠΎΠ°Ρ‚ΡŒ Ρ€Π°Π·Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΡ‹ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² Field для отобраТСния Π΄Π°Π½Π½Ρ‹Ρ…: число, Π΄Π°Ρ‚Π°, ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅, HTML, массив, ссылка ΠΈ Π΄Ρ€ΡƒΠ³ΠΈΠ΅.

НапримСр, ΠΏΠΎΠ»Π΅ с адрСсом Π²Π΅Π±-сайта выглядит ΠΊΠ°ΠΊ URL. ВмСсто Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒ Π΅Π³ΠΎ Π² Π²ΠΈΠ΄Π΅ тСкста, ΠΏΠΎΡ‡Π΅ΠΌΡƒ Π±Ρ‹ Π½Π΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°Π·ΠΈΡ‚ΡŒ Π΅Π³ΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ кликабСльной ссылки? Π­Ρ‚ΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ Ρ‚ΠΎ, Ρ‡Ρ‚ΠΎ Π΄Π΅Π»Π°Π΅Ρ‚ <UrlField>.

Π’ react-admin поля ΡΠ²Π»ΡΡŽΡ‚ΡΡ простыми ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°ΠΌΠΈ React. ΠŸΡ€ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠΈ, ΠΎΠ½ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°ΡŽΡ‚ record ΠΈΠ· API (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, { "id": 2, "name": "Ervin Howell", "website": "anastasia.net", ... }), ΠΈ source ΠΏΠΎΠ»Π΅ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΎΡ‚ΠΎΠ±Ρ€Π°Π·ΠΈΡ‚ΡŒ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, website). Π­Ρ‚ΠΎ ΠΎΠ·Π½Π°Ρ‡Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π°ΠΏΠΈΡΠ°Ρ‚ΡŒ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ достаточно просто. НапримСр, Π²ΠΎΡ‚ упрощСнная вСрсия UrlField:

// in src/MyUrlField.js
import React from 'react';

const MyUrlField = ({ record = {}, source }) =>
  <a href={record[source]}>
    {record[source]}
  </a>;

export default MyUrlField;

Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ этот ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π² <UserList> вмСсто ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° <UrlField> ΠΈ ΠΎΠ½ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π°Π½Π°Π»ΠΎΠ³ΠΈΡ‡Π½ΠΎ.

// in src/users.js
import React from 'react';
import { List, Datagrid, TextField, EmailField } from 'react-admin';
import MyUrlField from './MyUrlField';

export const UserList = props => (
    <List {...props}>
        <Datagrid rowClick="edit">
            ...
            <MyUrlField source="website" />
            ...
        </Datagrid>
    </List>
);

Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π·Π°ΠΌΠ΅Π½ΠΈΡ‚ΡŒ любой ΠΈΠ· ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² react-admin своим собствСнным, Ссли встроСнный ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ Π½Π΅ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ вашим потрСбностям.

Настройка стилСй

ΠšΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ MyUrlField – прСкрасная Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΏΡ€ΠΎΠΈΠ»Π»ΡŽΡΡ‚Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ, ΠΊΠ°ΠΊ Π½Π°ΡΡ‚Ρ€Π°ΠΈΠ²Π°Ρ‚ΡŒ стили. React-admin опираСтся Π½Π° material-ui, Π½Π°Π±ΠΎΡ€ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ² React, созданный ΠΏΠΎ ΠΎΠ±Ρ€Π°Π·Ρ†Ρƒ Руководства Google ΠΏΠΎ UI-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅. Material-ui ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ JSS, Ρ€Π΅ΡˆΠ΅Π½ΠΈΠ΅ CSS-in-JS, для стилизации ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΎΠ². Π”Π°Π²Π°ΠΉΡ‚Π΅ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡΡ возмоТностями JSS, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΡƒΠ΄Π°Π»ΠΈΡ‚ΡŒ ΠΏΠΎΠ΄Ρ‡Π΅Ρ€ΠΊΠΈΠ²Π°Π½ΠΈΠ΅ ΠΈΠ· ссылки ΠΈ Π΄ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ Π·Π½Π°Ρ‡ΠΎΠΊ:

// in src/MyUrlField.js
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import LaunchIcon from '@material-ui/icons/Launch';

const useStyles = makeStyles({
  link: {
    textDecoration: 'none'
  },
  icon: {
    width: '0.5em',
    paddingLeft: 2
  }
});

const MyUrlField = ({ record = {}, source }) => {
  const classes = useStyles();
  return (
    <a href={record[source]} className={classes.link}>
      {record[source]}
      <LaunchIcon className={classes.icon} />
    </a>
  );
};

export default MyUrlField;

Π’ JSS стили ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡŽΡ‚ΡΡ ΠΊΠ°ΠΊ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ JavaScript, ΠΈΠΌΠ΅Π½ΡƒΡŽ свойства CSS Π² стилС JS (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, textDecoration вмСсто text-decoration). Π§Ρ‚ΠΎΠ±Ρ‹ ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‚ΡŒ эти стили ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρƒ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ makeStyles для Π³Π΅Π½Π΅Ρ€Π°Ρ†ΠΈΠΈ React-Ρ…ΡƒΠΊΠ°. Π₯ΡƒΠΊ создаст Π½ΠΎΠ²Ρ‹Π΅ ΠΈΠΌΠ΅Π½Π° классов для этих стилСй ΠΈ Π²Π΅Ρ€Π½Π΅Ρ‚ Π½ΠΎΠ²Ρ‹Π΅ ΠΈΠΌΠ΅Π½Π° классов Π² ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π΅ классов. Π—Π°Ρ‚Π΅ΠΌ Π²Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эти ΠΈΠΌΠ΅Π½Π° className prop, ΠΊΠ°ΠΊ Ссли Π±Ρ‹ Π²Ρ‹ использовали ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹ΠΉ класс CSS.

Π’ JSS Π΅ΡΡ‚ΡŒ Π³ΠΎΡ€Π°Π·Π΄ΠΎ большС возмоТностСй, ΠΎ ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΌΠΎΠΆΠ½ΠΎ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π² Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚Π°Ρ†ΠΈΠΈ ΠΏΠΎ Material-UI. Π’Π°ΠΊ ΠΆΠ΅ Material-UI ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Π΄Ρ€ΡƒΠ³ΠΈΠ΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ CSS-in-JS, Π²ΠΊΠ»ΡŽΡ‡Π°Ρ Styled Components.

ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° взаимосвязСй

Π’ нашСм Ρ„ΠΈΠΊΡ‚ΠΈΠ²Π½ΠΎΠΌ API ΠΎΡ‚ JSONPlaceholder содСрТатся записи ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΉ post ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ содСрТат ΠΏΠΎΠ»Π΅ userId ΡƒΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‰Π΅Π΅ Π½Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ. Π­Ρ‚ΠΎ позволяСт ΠΏΡ€ΠΎΠ΄Π΅ΠΌΠΎΠ½ΡΡ‚Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π°Π±ΠΎΡ‚Ρƒ со взаимосвязями Π² Π΄Π°Π½Π½Ρ‹Ρ….

React-admin Π·Π½Π°Π΅Ρ‚, ΠΊΠ°ΠΊ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ эти внСшниС ΠΊΠ»ΡŽΡ‡ΠΈ для получСния ссылок. Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΠΌ, ΠΊΠ°ΠΊ ListGuesser управляСт ΠΈΠΌΠΈ, создав Π½ΠΎΠ²Ρ‹ΠΉ <Resource> для ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΉ Ρ‚ΠΎΡ‡ΠΊΠΈ API /posts:

...
import { Admin, Resource, ListGuesser } from 'react-admin';
...
const App = () => (
  <Admin dataProvider={dataProvider} authProvider={authProvider}>
    <Resource name='posts' list={ListGuesser} />
    ...
</Admin>
);
...

ListGuesser ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ для поля userId. Π”Π°Π²Π°ΠΉΡ‚Π΅ ΠΏΠΎΠΈΠ³Ρ€Π°Π΅ΠΌ с этим Π½ΠΎΠ²Ρ‹ΠΌ ΠΏΠΎΠ»Π΅ΠΌ, создав ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ PostList Π½Π° основС ΠΊΠΎΠ΄Π°, ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½Π½ΠΎΠ³ΠΎ гэссСром:

// in src/posts.js
import React from 'react';
import { List, Datagrid, TextField, ReferenceField } from 'react-admin';

export const PostList = props => (
  <List {...props}>
    <Datagrid rowClick='edit'>
      <ReferenceField source='userId' reference='users'>
        <TextField source='id' />
      </ReferenceField>
      <TextField source='id' />
      <TextField source='title' />
      <TextField source='body' />
    </Datagrid>
  </List>
);
// in src/App.js
...
import { Admin, Resource } from 'react-admin';
import { PostList } from './posts';
...
<Resource name="posts" list={PostList} />
...

ΠŸΡ€ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΈ списка сообщСний ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅Ρ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π°Π²Ρ‚ΠΎΡ€Π° сообщСния Π² Π²ΠΈΠ΄Π΅ <TextField>. Π­Ρ‚ΠΎ ΠΏΠΎΠ»Π΅ id Π½Π΅ ΠΈΠΌΠ΅Π΅Ρ‚ большого значСния, вмСсто этого Π΄Π°Π²Π°ΠΉΡ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ имя ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ. Для этого Π² src/posts.js вмСсто <TextField source="id" /> поставим <TextField source="name" />.

// in src/posts.js
...
<ReferenceField source='userId' reference='users'>
    <TextField source='name' />
</ReferenceField>
...

Бписок сообщСний Ρ‚Π΅ΠΏΠ΅Ρ€ΡŒ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅Ρ‚ ΠΈΠΌΠ΅Π½Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΉ строкС.

Для справки: ΡΠ°ΠΌΠΎΡΡ‚ΠΎΡΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ <ReferenceField> Π½ΠΈΡ‡Π΅Π³ΠΎ Π½Π΅ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅Ρ‚. Он просто Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ связанныС Π΄Π°Π½Π½Ρ‹Π΅ ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅Ρ‚ ΠΈΡ… Π² Π²ΠΈΠ΄Π΅  record  своСму Π΄ΠΎΡ‡Π΅Ρ€Π½Π΅ΠΌΡƒ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρƒ (Π² нашСм случаС это <TextField>). Как ΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ <List>, всС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ <Reference> ΠΎΡ‚Π²Π΅Ρ‡Π°ΡŽΡ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π·Π° Π²Ρ‹Π±ΠΎΡ€ΠΊΡƒ ΠΈ ΠΏΠΎΠ΄Π³ΠΎΡ‚ΠΎΠ²ΠΊΡƒ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ Π΄Π΅Π»Π΅Π³ΠΈΡ€ΡƒΡŽΡ‚ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³ своим Π΄ΠΎΡ‡Π΅Ρ€Π½ΠΈΠΌ элСмСнтам.

ΠžΠ±Ρ€Π°Ρ‚ΠΈΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅: Ссли Π²Ρ‹ посмотритС Π½Π° Π²ΠΊΠ»Π°Π΄ΠΊΡƒ network вашСго Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π°, Ρ‚ΠΎ Π·Π°ΠΌΠ΅Ρ‚ΠΈΡ‚Π΅, Ρ‡Ρ‚ΠΎ react-admin ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ запросы ΠΊ /user ΠΈ выполняСт ΠΎΠ΄ΠΈΠ½ HTTP-запрос для получСния всСй Ρ‚Π°Π±Π»ΠΈΡ†Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…. Π­Ρ‚Π° оптимизация Π΄Π΅Π»Π°Π΅Ρ‚ интСрфСйс Π±ΠΎΠ»Π΅Π΅ быстрым ΠΈ ΠΎΡ‚Π·Ρ‹Π²Ρ‡ΠΈΠ²Ρ‹ΠΌ.

Π§Ρ‚ΠΎΠ±Ρ‹ Π·Π°Π²Π΅Ρ€ΡˆΠΈΡ‚ΡŒ список ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΉ, помСститС ΠΏΠΎΠ»Π΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€Π° записи id Π² качСствС ΠΏΠ΅Ρ€Π²ΠΎΠ³ΠΎ столбца ΠΈ ΡƒΠ΄Π°Π»ΠΈΡ‚Π΅ ΠΏΠΎΠ»Π΅ body. Π‘ Ρ‚ΠΎΡ‡ΠΊΠΈ зрСния UX поля, большим частям тСкста Π½Π΅ стоит ΠΏΠΎΡΠ²Π»ΡΡ‚ΡŒΡΡ Π² Ρ‚Π°Π±Π»ΠΈΡ†Π΅ Π΄Π°Π½Π½Ρ‹Ρ…, Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Π² ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Ρ‹Ρ… прСдставлСниях. ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π²Ρ‹Π΄Π΅Π»ΠΈΡ‚ΡŒ дСйствиС Β«ΠŸΡ€Π°Π²ΠΊΠ°Β» (Edit), Π΄Π°Π²Π°ΠΉΡ‚Π΅ Π·Π°ΠΌΠ΅Π½ΠΈΠΌ rowClick ΠΊΠ½ΠΎΠΏΠΊΠΎΠΉ явного дСйствия:

// in src/posts.js
import React from 'react';
import { List, Datagrid, TextField, ReferenceField, EditButton } from 'react-admin';

export const PostList = props => (
  <List {...props}>
    <Datagrid>
      <TextField source='id' />
      <ReferenceField source='userId' reference='users'>
        <TextField source='name' />
      </ReferenceField>
      <TextField source='title' />
      <EditButton />
    </Datagrid>
  </List>
);

Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ возмоТностСй создания ΠΈ рСдактирования ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΡŽ

ΠŸΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ рассмотрСнному Ρ€Π°Π½Π΅Π΅ Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Π΅ΠΉ, ΠΌΠΎΠΆΠ½ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ ΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ добавлСния ΠΈ рСдактирования ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΉ. Для это ΠΏΡ€ΠΈΠΌΠ΅Π½ΠΈΠΌ Π·Π½Π°ΠΊΠΎΠΌΡ‹ΠΉ <EditGuesser>, Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ структуру ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π°, Π° Π·Π°Ρ‚Π΅ΠΌ Π½Π° Π΅Ρ‘ основС создадим ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΡΠΊΠΈΠΉ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ ΠΈ, ΠΏΡ€ΠΈ нСобходимости, внСсСм Π² Π½Π΅Π³ΠΎ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹.

export const PostEdit = props => (
  <Edit {...props}>
    <SimpleForm>
      <TextInput disabled source='id' />
      <ReferenceInput source='userId' reference='users'>
        <SelectInput optionText='name' />
      </ReferenceInput>
      <TextInput source='title' />
      <TextInput multiline source='body' />
    </SimpleForm>
  </Edit>
);

Для Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ создания Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅ΠΌ PostCreate:

export const PostCreate = props => (
  <Create {...props}>
    <SimpleForm>
      <ReferenceInput source='userId' reference='users'>
        <SelectInput optionText='name' />
      </ReferenceInput>
      <TextInput source='title' />
      <TextInput multiline source='body' />
    </SimpleForm>
  </Create>
);

РСгистрируСм ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Ρ‹ Π² App.js ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Ρ€Π°Π±ΠΎΡ‡ΠΈΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» рСдактирования ΠΈ добавлСния.

import React from 'react';
import { Admin, Resource } from 'react-admin';
import authProvider from './authProvider';
import { PostList, PostEdit, PostCreate } from './posts';
import { UserList, UserEdit, UserCreate } from './users';
import jsonServerProvider from 'ra-data-json-server';

const dataProvider = jsonServerProvider('https://jsonplaceholder.typicode.com');

const App = () => (
  <Admin dataProvider={dataProvider} authProvider={authProvider}>
    <Resource name='posts' list={PostList} edit={PostEdit} create={PostCreate} />
    <Resource name='users' list={UserList} edit={UserEdit} create={UserCreate} />
  </Admin>
);

export default App;

Π‘Ρ‚Ρ€Π°Π½ΠΈΡ†Π° рСдактирования ΠΈΠΌΠ΅Π΅Ρ‚ Π½Π΅Π±ΠΎΠ»ΡŒΡˆΡƒΡŽ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡƒ: ΠΎΠ½Π° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ сообщСния Π² качСствС основного Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠ° (тСкст, ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Π΅ΠΌΡ‹ΠΉ Π² Π²Π΅Ρ€Ρ…Π½Π΅ΠΉ ΠΏΠ°Π½Π΅Π»ΠΈ). Π”Π°Π²Π°ΠΉΡ‚Π΅ настроим Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΎΠΊ прСдставлСния с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° title:

// in src/posts.js
...
const PostTitle = ({ record }) => {
  return <span>Post {record ? `"${record.title}"` : ''}</span>;
};

export const PostEdit = props => (
  <Edit title={<PostTitle />} {...props}>
...
  </Edit>
);

На этом этапС ΠΌΡ‹ ΡƒΠΆΠ΅ ΠΈΠΌΠ΅Π΅ΠΌ достаточно Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠ΅ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΠΏΡƒΡΡ‚ΡŒ Ρ…ΠΎΡ‚ΡŒ ΠΏΠΎΠΊΠ° ΠΈ с ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ΠΌ ΠΊ Ρ„ΠΈΠΊΡ‚ΠΈΠ²Π½ΠΎΠΌΡƒ API. Π’ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅ΠΉ части ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ рассмотрим Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ поиска ΠΈ Ρ„ΠΈΠ»ΡŒΡ‚Ρ€ΠΎΠ², ΠΊΠ°ΡΡ‚ΠΎΠΌΠΈΠ·Π°Ρ†ΠΈΡŽ мСню ΠΈ домашнСй страницы, Π° Ρ‚Π°ΠΊ ΠΆΠ΅ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ Π½Π° ΠΌΠΎΠ±ΠΈΠ»ΡŒΠ½Ρ‹Ρ… устройствах.

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *

Π­Ρ‚ΠΎΡ‚ сайт ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ Akismet для Π±ΠΎΡ€ΡŒΠ±Ρ‹ со спамом. Π£Π·Π½Π°ΠΉΡ‚Π΅, ΠΊΠ°ΠΊ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ ваши Π΄Π°Π½Π½Ρ‹Π΅ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠ΅Π².