marmelab / react-admin

A frontend Framework for single-page applications on top of REST/GraphQL APIs, using TypeScript, React and Material Design
http://marmelab.com/react-admin
MIT License
24.78k stars 5.22k forks source link

4.0 Roadmap #5933

Closed fzaninotto closed 2 years ago

fzaninotto commented 3 years ago

We're starting to think about the next major milestone for react-admin. It'll mostly be a cleanup release, removing all the deprecated features and the compatibility layers we put in place. It should also be the occasion to use the latest major version of our dependencies.

Breaking Changes

These items didn't make it into the release:

We won't break BC for another year or two after 4.0, so we'd better wait to incorporate the new major releases of our dependencies. This causes the following releases to be blockers:

Timeline

Work on v4 started in September 2021, and is expected to end in early 2022.

Note: Please don't post comments with feature requests that don't need a breaking change here. React-admin releases new features on a monthly basis, and doesn't need a major release for that. This discussion is only for backwards-incompatible changes.

zhan-ge commented 3 years ago

@infilos Can you be a little more specific?

I just want build more than one dashboard component and pass them as parameters to Admin component, eg.

<Admin dashboard={OrderDashboard, ItemDashboard}></Admin>
djhi commented 3 years ago

And how do you expect react-admin to choose which one to display ?

zhan-ge commented 3 years ago

And how do you expect react-admin to choose which one to display ?

Yeah, list all the dashboard in the sidebar, just like resource does, then I can build fully admin/monitor/mange application in one react-admin app, without creating another pure react app just for some more dashboards/charts...

fzaninotto commented 3 years ago

So you basically want to add custom pages? If so, this already exists (check the customRoutes prop). Also, this isn't valid JSX:

<Admin dashboard={OrderDashboard, ItemDashboard}></Admin>
fzaninotto commented 3 years ago

Now that we de-emphasize element cloning in favor of contexts, should we reconsider #3246 (injecting Components instead of Elements)?

andrico1234 commented 3 years ago

Removing use of lodash.get for two reasons.

  1. AFAIK most lodash.get functionality can be replicated using the optional chaining parameter
  2. We lose type inference when using lodash.get, using a type safe alternative will mean we're less likely to be running into any types
fzaninotto commented 3 years ago

@andrico1234 This is not a BC break. See the issue description:

Please don't post comments with feature requests that don't need a breaking change here. React-admin releases new features on a monthly basis, and doesn't need a major release for that. This discussion is only for backwards-incompatible changes.

andrico1234 commented 3 years ago

My bad!

mxmvshnvsk commented 3 years ago

Hi. Do you have plans to translate documentation into other languages?

fzaninotto commented 3 years ago

@mxmvshnvsk No we don't. If you want to discuss about it, please open a new issue, this one is for breaking changes only.

panfiva commented 3 years ago

I'm adding one possible breaking change: updating the format of the filters parameter passed to getList.

Currently, the filters are passed as key: value, e.g.:

{ filter: { name: "John Doe" } }

Which doesn't allow an operator. When developers want to use operators like greater than, includes, not equal, they currently have no standard way of doing so. We've got around this in demos by appending operators to the key name, but that's neither standard or explicit:

{ filter: { name_neq: "John Doe" } }

We should pass filters as an array of conditions, each condition being a field, an operator, and a value.

{ filter: [{ key: "name", operator: "NOT_EQUALS", value: "John Doe" }] }

dataProviders would then do what they want to convert that to their API syntax.

Cons: This would significantly increase the difficulty to build a dataProvider, and break all existing providers. Besides, not all backends support such operators, so basing a feature on the existence of these operators (like a date interval filter) would exclude some backends.

What do you think?

More complex filters would be nice; however since it breaks all existing providers, it could case a problem for many people. Should we just do { filter: { name: "John Doe", operator: "OP", }, where operator is optional and keep default behavior to match RA v3. For simplicity reasons, I would suggest only including support for equal, not equal operators. contains and not contains only.

For more advanced use cases, we can use { advancedFilter: [ {value:"John Doe"}, options: {...} ] }, where options an object of any type that application developers can define whichever way they like.

For example, today, I have a filter that searches for text in 3 fields - report_name, tab_name and tab_description. If i had this option, I would send the following object to getList:

 {
  pagination: { ...PaginationPayload },
  sort: { ...SortPayload },
  filter: {},
  advancedFilter: [
    {
      value: 'value i am search for',
      options: { name: ['report_name', 'tab_name', 'tab_description'], operator: 'CONTAINS' },
    },
  ],
  dataOptions: { fields: ['report_name', 'tab_name', 'id', 'tab_description', 'url'] },
}

In this particular approach, it will be my responsibility to define what advanced filters are supported by the application, and to properly implement data provider to utilize these filters.

Also note that i have dataOptions attribute - it was mentioned in https://github.com/marmelab/react-admin/issues/5933#issuecomment-864171782. In this particular example, i am restricting fields that are returned back by the data provider. Similar to advancedFilter, it is my responsibility to define dataOptions interface and implement data provider that properly handles these options.

panfiva commented 3 years ago

Would like to be able to sort by more that one column. For example, I have a list displaying account names. When searching for accounts, we get a list that contains both enabled and disabled accounts. Would like to be able to always sort by status (enabled first), and then by name. The following change would do the trick:

export interface GetListParams {
  pagination: PaginationPayload;
  sort: SortPayload[] | SortPayload; // individual SortPayload object is accepted for backward compatibility
  filter: any;
}

const sort:SortPayload[] =[ {field:"status", order:"desc"}, {field:"name", order:"asc"}]
macrozone commented 3 years ago

I'd love to see more typescript improvements.

Remove prop injection and child cloning, use context instead

should definitily help with that, as its currently tricky to type custom fields, inputs etc. properly.

Additionaly i would like to see:

using react-elements here often feels awkward. Also you can't use it like normal react-elements. E.g. <Resource /> has to be an immediate child of <Admin />, but you would expect to be able to wrap it in other components.

My biggest pain point is that you don't see typos, nor autocompletion when you write forms. For example, if you rename a field on the backend, you won't recognize this in react-admin. This might be very tricky to solve. A start would be that dataprovider can optionally be typed in the sense that they "know" the models and methods. The hard part is how to leaverage typescript to type check form fields. They would need to know on which resource they operate on.

The simplest way would be that they take the current Resource-model as a generic param. E.g. <TextInput<UserModel> source="firstName" />, which would complain if UserModel.firstName would not exist. Not sure if typescript is able to infer this from the surounding components (i doubt it). Other ideas would be to have some functions that return "typed" inputs and fields.

larsblumberg commented 3 years ago

It should also be the occasion to use the latest major version of our dependencies.

As you want to upgrade dependencies to their current mayor version, please consider moving from Apollo 2.6 to Apollo 3.x for the GraphQL data provider.

zahidraza commented 3 years ago

What about React 18? Will you wait for the release?

franz101 commented 3 years ago

React hook Form and Redux Toolkit would be amazing

djhi commented 3 years ago

We should avoid handling the submit on enter ourselves as it causes issues for some users. For example, Japanese people typing Kanji must first type the corresponding hiragana sounds, and then press enter to finalize it. Browsers will not submit the form in this case but we do.

franz101 commented 3 years ago

Load More pagination style for databases like dynamodb would be exciting there is an example here: https://github.com/mayteio/ra-aws-amplify

dejancg commented 3 years ago

Well gentlemen (and ladies?), MUI v5 is out and here is the migration guide: https://next.material-ui.com/guides/migration-v4/ I hope you guys were ready for the switch from JSS to emotion

dejancg commented 3 years ago

BTW, have you decided what are you going to use instead of react-final-form going forward? I've seen this bug (https://github.com/marmelab/react-admin/issues/6584) so I guess you guys can't wait to ditch react-final-form.

I wanted to experiment a bit and since I've heard some good stuff about react-hook-form, I wanted to give it a try. Here is an example component I was able to use with react-admin.

import React from "react";
import {
  FieldTitle,
  InputHelperText,
  ResettableTextField,
  ValidationErrorMessage,
} from "react-admin";
import { Controller, FieldValues } from "react-hook-form";
import { HookFormInputProps } from "./HookFormInputProps";

interface Props<TRecord> extends HookFormInputProps<TRecord> {
  options?: any;
  margin?: "none" | "dense" | "normal";
  variant?: "standard" | "outlined" | "filled";
}

declare module "react" {
  function forwardRef<T, P = {}>(
    render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
  ): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}

const HookFormTextInputComponent = <TRecord extends FieldValues>(
  {
    control,
    name,
    defaultValue,
    label,
    rules,
    margin,
    variant,
    options,
  }: Props<TRecord>,
  ref: React.Ref<unknown>
) => {
  return (
    <Controller
      control={control}
      name={name}
      defaultValue={defaultValue ?? ""}
      rules={rules}
      render={(renderProps) => {
        return (
          <ResettableTextField
            {...options}
            source={name}
            label={
              label !== "" &&
              label !== false && (
                <FieldTitle
                  label={label}
                  source={name}
                  isRequired={!!rules?.required}
                />
              )
            }
            error={
              !!(
                renderProps.fieldState.isTouched && renderProps.fieldState.error
              )
            }
            helperText={
              <InputHelperText
                touched={renderProps.fieldState.isTouched}
                error={
                  (renderProps.fieldState.error as any)
                    ?.message as ValidationErrorMessage
                }
                helperText={
                  (renderProps.fieldState.error as any)?.message as string
                }
              />
            }
            variant={options?.variant ?? variant}
            margin={options?.margin ?? margin}
            {...renderProps.field}
            ref={ref}
            inputRef={renderProps.field.ref}
          />
        );
      }}
    />
  );
};

export const HookFormTextInput = React.forwardRef(HookFormTextInputComponent);

This recent pull request: https://github.com/react-hook-form/react-hook-form/pull/6381 broke the fieldState.error type so I had to use (renderProps.fieldState.error as any)?.message.

I was able to use it like this:

<HookFormTextInput<TRecord>
        control={control}
        name="name"
        label="resources.admin-roles.labels.name"
        rules={{ required: translate("ra.validation.required") }}
        defaultValue={record?.name ?? ""}
      />

The control is from:

 const { control } = useFormContext<TRecord>();
fuunnx commented 2 years ago

Feature request : react-query for data fetching

Hi, I’m glad to see react-admin moving in this direction, being a valuable tool in my everyday life. Shoutout to the team !

Before using react-admin I was a big user of react-query for data fetching. It happens that the API of react-admin’s dataprovider hooks is really close to react-query’s. I believe this new release might be a great opportunity to fully migrate to react-query : both tools do respond to the same problems, but react-query being specialized around data fetching offers a lot flexibility and optimizations (see this blog post series)

I also believe that, delegating this part to an external library might reduce a lot of react-admin’s codebase size

Also, +1 for react-hook-form

crates commented 2 years ago

Is there any roadmap or time table for version 4 of React Admin? Especially with regard to contexts, and documentation around things like ResourceContextProvider and SaveContext, I am very eagerly awaiting more details. Let me know what you think about this, and thank you for all of your tremendous work on this excellent suite of tools!

crates commented 2 years ago

As far as nice-to-haves, I know this might be a bit of a reach, but if we could infer types and enums using similar tooling to GraphQL Code-Gen, I wouldn't have to keep doing all of that by hand on every project I set up with React Admin.

crates commented 2 years ago

Also, please do not release new features without their associated documentation. You've added a TON of new features like the aforementioned ResourceContextProvider and SaveContext, and stuff like CreateBase and ListBase and ShowBase have been included for nearly a year without any official documentation provided (you can't even find these things when searching the docs!) I've read some of your blog posts on some of these things, and that's a good start, but the right place for those details is on the documentation page: not on your blog. We need to be able to search for these details to understand them better, and the docs are still SUPER sparse on a LOT of recently-added features.

fzaninotto commented 2 years ago

@crates you missed an important part of this issue description:

Note: Please don't post comments with feature requests that don't need a breaking change here. React-admin releases new features on a monthly basis, and doesn't need a major release for that. This discussion is only for backwards-incompatible changes.

As far as infering types is concerned, this is the responsibility of guessers. If the react-admin guessers aren't enough, you can always replace them by your own. The API Platform admin package, for instance, offers a specialized dataProvider for the Hydra REST flavor. They've developed super smart guessers that rely on the schema (Hydra, like graphQL, exposes a schema) to get a very good first shot. So this is not a feature that depends on breaking backwards compatibility, and therefore it doesn't need to be discussed here.

About the documentation, it's the same: you don't need to wait for a major version to improve the documentation. So you can open a PR today on the master branch with your documentation suggestions. Or, if you're in dire need of new documentation, consider sponsoring the project so that more people can contribute to it in their day time - and not only free time.

crates commented 2 years ago

@fzaninotto: The person who's best-able to document what's been built is the person who built it. Not having anything to go on in terms of how to learn what's been built, how could I (or anyone else) add that documentation? It's only possible for the original contributors to adequately map out what they added to the software.

fzaninotto commented 2 years ago

@crates again, this has nothing to do with this issue, so please stop posting comments about documentation here.

To conclude this discussion, we do the best we can with our limited time. We know that documentatoin is important and that we probably are the best people to write it. But we're not the only people capable of writing it, and other tasks are important, too.

fzaninotto commented 2 years ago

FYI, I added a Timeline section to the description, as we've begun the work on v4 and intend to release it in early 2022

smeng9 commented 2 years ago

Are we gradually migrating the bundler of all example applications https://github.com/marmelab/react-admin/blob/master/examples from create-react-app/webpack to vite/rollup in the v4.0?

The README in that folder says the example apps are using create-react-app, but actually some of them are using vite

For simplicity, create-react-app is the official method to bootstrap a project in react community. Based on npm trends, the vite is indeed gaining popularity due to better developer experience, faster build time -> low CO2 emission.

franz101 commented 2 years ago

https://github.com/sindresorhus/type-fest/blob/main/source/get.d.ts

Implement Typecheck for path!!

franz101 commented 2 years ago

Also make show / edit layout more able to be in a grid: https://github.com/marmelab/react-admin/issues/3373

panfiva commented 2 years ago

Provide a way to override default base path for resource. Currently, resource name is as basePath. I want to be able to display these easily inside tabs:

Something along these lines

<Admin {...props} >
  <Resource name='products' basePath='/seach/retail_products' show={Show} list={List} />
  <Resource name='services' basePath='/seach/services' show={Show} list={List} />
</Admin>

this should register /search/product URLs within the application, while still using 'products' resource name in API calls

The reason i would like to be able to do that is to easily add resource pages inside tabs on a page. For example, i would like to build a search page that has several tabs and each tab contains list, show, edit, create, update endpoints.

fzaninotto commented 2 years ago

For your information, I've added a task about replacing Redux query cache with react-query. We should rely on the best in class libraries instead of maintaining our own.

jonArzelus commented 2 years ago

It seems that react-router has finally reached v6 @fzaninotto (just informing to update desc) https://www.npmjs.com/package/react-router Looking forward for RA4!

franz101 commented 2 years ago

For your information, I've added a task about replacing Redux query cache with react-query. We should rely on the best in class libraries instead of maintaining our own.

Have you looked into: https://redux-toolkit.js.org/rtk-query/api/createApi

fzaninotto commented 2 years ago

For your information, I've added a task about replacing Redux query cache with react-query. We should rely on the best in class libraries instead of maintaining our own.

Have you looked into: https://redux-toolkit.js.org/rtk-query/api/createApi

Yes, but we really want to avoid relying on Redux for data fetching. One of the main reasons is to allow people to serialize their redux state so that they can restart their session. If the state contains the fetched data, this isn't practical. The other reason is that testing Redux state is not fun at all (cf https://github.com/marmelab/react-admin/pull/6753).

djhi commented 2 years ago

Besides, it seems it doesn't provide the API we need around caching to make data from one request available as the initial data for another

franz101 commented 2 years ago

Any ideas for a custom fetch that is not related getOne or list etc. For example I want to fetch a binary or xml not a json.

Right now I have to dispatch the redux actions to make the app show its loading...

In my dashboard inbox lets say i want to add the button download pdf for customer.

panfiva commented 2 years ago

Would be nice if I could use all components List, Show, Edit and all data provider functions to work in in custom routes that do not have corresponding "Resource" registration. Today, we have a few REST API endpoint that we frequently call without creating a resource. I noticed that many react-admin features do not function unless an endpoint is registered as Resource

panfiva commented 2 years ago

Ability to override Title inside a child component - last rendered <Title> should win and deep nesting should work (we sometimes have 3 layers, each having its own title.

Today, they are aggregated. A proper fix will, most likely, require complete redesign of where title is stored and how it is updated.


function ParentComponent = (props) => {

 const conditional = props.display? <ConditionalChild /> : null

 return (
   <Fragment>
     <Title title="Title1">
     Welcome to ParentComponent 
     {conditional}

  </Fragment>
  )
}

function ConditionalChild = () => {

 <Fragment>
   <Title title="Title2">
   Welcome to ConditionalChild 
 </Fragment>

}

}
panfiva commented 2 years ago

Any chance we can now fix https://github.com/marmelab/react-admin/issues/5492, or the same complexities mentioned at https://github.com/marmelab/react-admin/issues/5492#issuecomment-723791621 still exist?

DjebbZ commented 2 years ago

One thing I'd love to see in v4 is overhauled TypeScript support. For now, the types are sometimes misleading, often "wrong" and rarely support generics. What do I mean by "wrong" ? Often RA components offers plenty of props to use, but not all combinations of props are valid. The way it's currently done is all required props are noted required in the type, but all optional fields are noted optional. What should be done is see which combination of props make sense together and create a type for each combination, then combine all these combinations with a type union.

Simplified example to illustrate the concept:

// Let's say the component should only take `a`, `a & b` or `a & c` props.

// wrong, currently in RA
export interface ComponentProps {
  a: string;
  b?: number;
  c?: boolean;
}

// right
interface ComponentPropsA {
  a: string;
}
type ComponentPropsB = ComponentPropsA & {
  b: number;
}
type ComponentPropsC = ComponentPropsA & {
  c: boolean;
}

export type ComponentProps = ComponentPropsA | ComponentPropsB | ComponentProps C;

It's clearly more code and more work on RA's team behalf, but that's where the true power of static typing comes from in the sense that the type system ensures that only valid states are accepted and invalid states are impossible.

This is my biggest gripe with RA so far (and custom form handling but you've already planned to move to react-hook-form handling which should solve most problems).

fzaninotto commented 2 years ago

@DjebbZ the fact that we're removing props injection helps a lot to fix TypeScript types. You can already test it in the next branch. As for union types, do you have specific examples of components that could benefit from them?

meastp commented 2 years ago

I also wish for more options when implementing pagination, to support e.g. keyset pagination and other techniques. This is possible to implement if the next and previous links could contain information as string (an integer, as it is now, makes this impossible). Perhaps this is too late, since you've come far with RA4?

djhi commented 2 years ago

Definitely something we want to add. However, we can probably do it without breaking changes

fzaninotto commented 2 years ago

@meastp Agreed. This is already identified and in the roadmap ('add support for cursor-based pagination').

meastp commented 2 years ago

@djhi @fzaninotto Great! Feel free to ping me if you want an extra set of eyes (on an issue/PR) when implementing this :)

meastp commented 2 years ago

Another (huge effort?) request would be to use react-table which is made by the react-query author. It is entirely non-ui and based on hooks, and could provide a lot of user customisation points (because of the hooks) and features to the built-in react-admin datagrids.

I realize this isn't strictly backwards incompatible in the sense that I can re-build ra components using react-table, but I think having this built-in would be something to consider to outsource some of the more advanced datagrid features. Just a thought :)

fzaninotto commented 2 years ago

@meastp Great idea, but we'll not address this in 4.0, as it can be developed in a standalone package without breaking BC in the core. By the way, if you feel like giving it a try, we'd love to see the result!

AntonOfTheWoods commented 2 years ago

We seem to be on Alphas, is there any idea when we will start seeing RCs (so stable API)?