Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFR] Remove FC usage from ra-core components #6515

Merged
merged 6 commits into from
Aug 30, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions packages/ra-core/src/auth/Authenticated.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cloneElement, ReactElement, FunctionComponent } from 'react';
import { cloneElement, ReactElement } from 'react';

import useAuthenticated from './useAuthenticated';

@@ -36,12 +36,13 @@ interface Props {
* </Admin>
* );
*/
const Authenticated: FunctionComponent<Props> = ({
authParams,
children,
location, // kept for backwards compatibility, unused
...rest
}) => {
const Authenticated = (props: Props) => {
const {
authParams,
children,
location, // kept for backwards compatibility, unused
...rest
} = props;
useAuthenticated(authParams);
// render the child even though the useAuthenticated() call isn't finished (optimistic rendering)
// the above hook will log out if the authProvider doesn't validate that the user is authenticated
31 changes: 13 additions & 18 deletions packages/ra-core/src/auth/WithPermissions.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
import {
Children,
FunctionComponent,
ReactElement,
ComponentType,
createElement,
} from 'react';
import { Children, ReactElement, ComponentType, createElement } from 'react';
import { Location } from 'history';

import warning from '../util/warning';
@@ -65,14 +59,15 @@ const isEmptyChildren = children => Children.count(children) === 0;
* </Admin>
* );
*/
const WithPermissions: FunctionComponent<Props> = ({
authParams,
children,
render,
component,
staticContext,
...props
}) => {
const WithPermissions = (props: Props) => {
const {
authParams,
children,
render,
component,
staticContext,
...rest
} = props;
warning(
(render && children && !isEmptyChildren(children)) ||
(render && component) ||
@@ -84,15 +79,15 @@ const WithPermissions: FunctionComponent<Props> = ({
const { permissions } = usePermissionsOptimized(authParams);
// render even though the usePermissions() call isn't finished (optimistic rendering)
if (component) {
return createElement(component, { permissions, ...props });
return createElement(component, { permissions, ...rest });
}
// @deprecated
if (render) {
return render({ permissions, ...props });
return render({ permissions, ...rest });
}
// @deprecated
if (children) {
return children({ permissions, ...props });
return children({ permissions, ...rest });
}
};

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionComponent, ReactElement } from 'react';
import { ReactElement } from 'react';

import useReferenceArrayFieldController from './useReferenceArrayFieldController';
import { ListControllerProps } from '../useListController';
@@ -22,7 +22,7 @@ interface Props {
*
* @see useReferenceArrayFieldController
*/
const ReferenceArrayFieldController: FunctionComponent<Props> = props => {
const ReferenceArrayFieldController = (props: Props) => {
const { children, ...rest } = props;
const controllerProps = useReferenceArrayFieldController({
sort: {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FunctionComponent, ReactNode, ReactElement } from 'react';
import { ReactNode, ReactElement } from 'react';
import get from 'lodash/get';

import { Record } from '../../types';
@@ -49,12 +49,12 @@ interface Props {
* <TextField source="name" />
* </ReferenceField>
*/
export const ReferenceFieldController: FunctionComponent<Props> = ({
export const ReferenceFieldController = ({
children,
record,
source,
...props
}) => {
}: Props) => {
const id = get(record, source);
return children({
...useReference({ ...props, id }),
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ReactElement, FunctionComponent } from 'react';
import { ReactElement } from 'react';

import { Record, SortPayload } from '../../types';
import useReferenceManyFieldController from './useReferenceManyFieldController';
@@ -24,7 +24,7 @@ interface Props {
*
* @see useReferenceManyFieldController
*/
export const ReferenceManyFieldController: FunctionComponent<Props> = props => {
export const ReferenceManyFieldController = (props: Props) => {
const { children, page = 1, perPage = 25, ...rest } = props;
const controllerProps = useReferenceManyFieldController({
page,
15 changes: 4 additions & 11 deletions packages/ra-core/src/controller/input/ReferenceInputController.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
ReactNode,
ComponentType,
FunctionComponent,
ReactElement,
} from 'react';
import { ReactNode, ComponentType, ReactElement } from 'react';

import { SortPayload, Record } from '../../types';
import {
@@ -33,11 +28,9 @@ interface Props {
*
* @see useReferenceInputController
*/
export const ReferenceInputController: FunctionComponent<Props> = ({
children,
...props
}) => {
return children(useReferenceInputController(props)) as ReactElement;
export const ReferenceInputController = (props: Props) => {
const { children, ...rest } = props;
return children(useReferenceInputController(rest)) as ReactElement;
};

export default ReferenceInputController as ComponentType<Props>;
47 changes: 24 additions & 23 deletions packages/ra-core/src/core/CoreAdmin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { FunctionComponent, ComponentType } from 'react';
import { ComponentType } from 'react';

import CoreAdminContext from './CoreAdminContext';
import CoreAdminUI from './CoreAdminUI';
@@ -86,28 +86,29 @@ export type ChildrenFunction = () => ComponentType[];
* );
* };
*/
const CoreAdmin: FunctionComponent<AdminProps> = ({
appLayout,
authProvider,
catchAll,
children,
customReducers,
customRoutes = [],
customSagas,
dashboard,
dataProvider,
disableTelemetry,
history,
i18nProvider,
initialState,
layout,
loading,
loginPage,
logoutButton,
menu, // deprecated, use a custom layout instead
theme,
title = 'React Admin',
}) => {
const CoreAdmin = (props: AdminProps) => {
const {
appLayout,
authProvider,
catchAll,
children,
customReducers,
customRoutes = [],
customSagas,
dashboard,
dataProvider,
disableTelemetry,
history,
i18nProvider,
initialState,
layout,
loading,
loginPage,
logoutButton,
menu, // deprecated, use a custom layout instead
theme,
title = 'React Admin',
} = props;
return (
<CoreAdminContext
authProvider={authProvider}
23 changes: 12 additions & 11 deletions packages/ra-core/src/core/CoreAdminContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { FunctionComponent, ComponentType, useContext, useState } from 'react';
import { ComponentType, useContext, useState } from 'react';
import { Provider, ReactReduxContext } from 'react-redux';
import { History } from 'history';
import { createHashHistory } from 'history';
@@ -40,16 +40,17 @@ export interface AdminContextProps {
theme?: object;
}

const CoreAdminContext: FunctionComponent<AdminContextProps> = ({
authProvider,
dataProvider,
i18nProvider,
children,
history,
customReducers,
customSagas,
initialState,
}) => {
const CoreAdminContext = (props: AdminContextProps) => {
const {
authProvider,
dataProvider,
i18nProvider,
children,
history,
customReducers,
customSagas,
initialState,
} = props;
const reduxIsAlreadyInitialized = !!useContext(ReactReduxContext);

if (!dataProvider) {
3 changes: 1 addition & 2 deletions packages/ra-core/src/core/CoreAdminRouter.tsx
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@ import React, {
createElement,
ComponentType,
ReactElement,
FunctionComponent,
} from 'react';
import { Route, Switch } from 'react-router-dom';

@@ -36,7 +35,7 @@ export interface AdminRouterProps extends CoreLayoutProps {

type State = ResourceElement[];

const CoreAdminRouter: FunctionComponent<AdminRouterProps> = props => {
const CoreAdminRouter = (props: AdminRouterProps) => {
const getPermissions = useGetPermissions();
const doLogout = useLogout();
const { authenticated } = useAuthState();
44 changes: 19 additions & 25 deletions packages/ra-core/src/core/CoreAdminUI.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
import * as React from 'react';
import {
createElement,
FunctionComponent,
ComponentType,
useMemo,
useEffect,
} from 'react';
import { createElement, ComponentType, useMemo, useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';

import CoreAdminRouter from './CoreAdminRouter';
@@ -24,9 +18,7 @@ import {

export type ChildrenFunction = () => ComponentType[];

const DefaultLayout: FunctionComponent<CoreLayoutProps> = ({ children }) => (
<>{children}</>
);
const DefaultLayout = ({ children }: CoreLayoutProps) => <>{children}</>;

export interface AdminUIProps {
catchAll?: CatchAllComponent;
@@ -47,21 +39,23 @@ export interface AdminUIProps {
// for BC
export type CoreAdminUIProps = AdminUIProps;

const CoreAdminUI: FunctionComponent<AdminUIProps> = ({
catchAll = Noop,
children,
customRoutes = [],
dashboard,
disableTelemetry = false,
layout = DefaultLayout,
loading = Noop,
loginPage = false,
logout,
menu, // deprecated, use a custom layout instead
ready = Ready,
theme,
title = 'React Admin',
}) => {
const CoreAdminUI = (props: AdminUIProps) => {
const {
catchAll = Noop,
children,
customRoutes = [],
dashboard,
disableTelemetry = false,
layout = DefaultLayout,
loading = Noop,
loginPage = false,
logout,
menu, // deprecated, use a custom layout instead
ready = Ready,
theme,
title = 'React Admin',
} = props;

const logoutElement = useMemo(() => logout && createElement(logout), [
logout,
]);
53 changes: 27 additions & 26 deletions packages/ra-core/src/core/Resource.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { FunctionComponent, useEffect, useMemo } from 'react';
import { useEffect, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Route, Switch } from 'react-router-dom';

@@ -10,15 +10,16 @@ import { ResourceContextProvider } from './ResourceContextProvider';

const defaultOptions = {};

const ResourceRegister: FunctionComponent<ResourceProps> = ({
name,
list,
create,
edit,
show,
icon,
options = defaultOptions,
}) => {
const ResourceRegister = (props: ResourceProps) => {
const {
name,
list,
create,
edit,
show,
icon,
options = defaultOptions,
} = props;
const dispatch = useDispatch();
useEffect(() => {
dispatch(
@@ -37,15 +38,16 @@ const ResourceRegister: FunctionComponent<ResourceProps> = ({
return null;
};

const ResourceRoutes: FunctionComponent<ResourceProps> = ({
name,
match,
list,
create,
edit,
show,
options = defaultOptions,
}) => {
const ResourceRoutes = (props: ResourceProps) => {
const {
name,
match,
list,
create,
edit,
show,
options = defaultOptions,
} = props;
const isRegistered = useSelector(
(state: ReduxState) => !!state.admin.resources[name]
);
@@ -141,14 +143,13 @@ const ResourceRoutes: FunctionComponent<ResourceProps> = ({
}, [basePath, name, create, edit, list, show, options, isRegistered]); // eslint-disable-line react-hooks/exhaustive-deps
};

const Resource: FunctionComponent<ResourceProps> = ({
intent = 'route',
...props
}) =>
intent === 'registration' ? (
<ResourceRegister {...props} />
const Resource = (props: ResourceProps) => {
const { intent = 'route', ...rest } = props;
return intent === 'registration' ? (
<ResourceRegister {...rest} />
) : (
<ResourceRoutes {...props} />
<ResourceRoutes {...rest} />
);
};

export default Resource;
16 changes: 3 additions & 13 deletions packages/ra-core/src/core/RoutesWithLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import React, {
Children,
cloneElement,
createElement,
FunctionComponent,
} from 'react';
import React, { Children, cloneElement, createElement } from 'react';
import { Redirect, Route, Switch } from 'react-router-dom';

import WithPermissions from '../auth/WithPermissions';
@@ -25,13 +20,8 @@ interface Props {

const defaultAuthParams = { route: 'dashboard' };

const RoutesWithLayout: FunctionComponent<Props> = ({
catchAll,
children,
customRoutes,
dashboard,
title,
}) => {
const RoutesWithLayout = (props: Props) => {
const { catchAll, children, customRoutes, dashboard, title } = props;
const childrenAsArray = React.Children.toArray(children);
const firstChild: React.ReactElement<any> | null =
childrenAsArray.length > 0
6 changes: 2 additions & 4 deletions packages/ra-core/src/dataProvider/Mutation.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { FunctionComponent } from 'react';

import useMutation from './useMutation';

interface ChildrenFuncParams {
@@ -53,15 +51,15 @@ interface Props {
* </Mutation>
* );
*/
const Mutation: FunctionComponent<Props> = ({
const Mutation = ({
children,
type,
resource,
payload,
// Provides an undefined onSuccess just so the key `onSuccess` is defined
// This is used to detect options in useDataProvider
options = { onSuccess: undefined },
}) =>
}: Props) =>
children(
...useMutation(
{ type, resource, payload },
12 changes: 3 additions & 9 deletions packages/ra-core/src/form/FormDataConsumer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { ReactNode, FunctionComponent } from 'react';
import { ReactNode } from 'react';
import { useFormState } from 'react-final-form';
import { FormSubscription } from 'final-form';
import get from 'lodash/get';
@@ -74,14 +74,8 @@ const FormDataConsumer = ({ subscription, ...props }: ConnectedProps) => {
return <FormDataConsumerView formData={formState.values} {...props} />;
};

export const FormDataConsumerView: FunctionComponent<Props> = ({
children,
form,
formData,
source,
index,
...rest
}) => {
export const FormDataConsumerView = (props: Props) => {
const { children, form, formData, source, index, ...rest } = props;
let scopedFormData = formData;
let getSource;
let getSourceHasBeenCalled = false;
17 changes: 6 additions & 11 deletions packages/ra-core/src/form/FormField.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import { FunctionComponent } from 'react';
import PropTypes from 'prop-types';
import { Field, FieldProps, FieldRenderProps } from 'react-final-form';
import { Validator, composeValidators } from './validate';
@@ -25,12 +24,8 @@ interface Props
validate?: Validator | Validator[];
}

const FormField: FunctionComponent<Props> = ({
id,
input,
validate,
...props
}) => {
const FormField = (props: Props) => {
const { id, input, validate, ...rest } = props;
if (process.env.NODE_ENV !== 'production') {
console.log('FormField is deprecated, use the useInput hook instead.');
}
@@ -39,15 +34,15 @@ const FormField: FunctionComponent<Props> = ({
? composeValidators(validate)
: validate;

const finalId = id || props.source;
const finalId = id || rest.source;

return input ? ( // An ancestor is already decorated by Field
React.createElement(props.component, { input, id: finalId, ...props })
React.createElement(rest.component, { input, id: finalId, ...rest })
) : (
<Field
{...props}
{...rest}
id={finalId}
name={props.source}
name={rest.source}
isRequired={isRequired(validate)}
validate={sanitizedValidate}
/>
4 changes: 2 additions & 2 deletions packages/ra-core/src/form/ValidationError.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import { FunctionComponent } from 'react';
import {
ValidationErrorMessage,
ValidationErrorMessageWithArgs,
@@ -10,7 +9,8 @@ interface Props {
error: ValidationErrorMessage;
}

const ValidationError: FunctionComponent<Props> = ({ error }) => {
const ValidationError = (props: Props) => {
const { error } = props;
const translate = useTranslate();
if ((error as ValidationErrorMessageWithArgs).message) {
const { message, args } = error as ValidationErrorMessageWithArgs;
10 changes: 3 additions & 7 deletions packages/ra-core/src/i18n/TranslationProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import React, {
useCallback,
useMemo,
Children,
FunctionComponent,
} from 'react';
import React, { useCallback, useMemo, Children, ReactNode } from 'react';

import { useSafeSetState } from '../util/hooks';
import { TranslationContext } from './TranslationContext';
@@ -12,6 +7,7 @@ import { I18nProvider } from '../types';
interface Props {
locale?: string;
i18nProvider: I18nProvider;
children: ReactNode;
}

interface State {
@@ -31,7 +27,7 @@ interface State {
* </Provider>
* );
*/
const TranslationProvider: FunctionComponent<Props> = props => {
const TranslationProvider = (props: Props) => {
const { i18nProvider, children } = props;

const [state, setState] = useSafeSetState<State>({
10 changes: 3 additions & 7 deletions packages/ra-core/src/util/FieldTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import { FunctionComponent, ReactElement, memo } from 'react';
import { ReactElement, memo } from 'react';

import useTranslate from '../i18n/useTranslate';
import getFieldLabelTranslationArgs from './getFieldLabelTranslationArgs';
@@ -11,12 +11,8 @@ interface Props {
label?: string | ReactElement | false;
}

export const FieldTitle: FunctionComponent<Props> = ({
resource,
source,
label,
isRequired,
}) => {
export const FieldTitle = (props: Props) => {
const { resource, source, label, isRequired } = props;
const translate = useTranslate();

if (label === false || label === '') {
51 changes: 26 additions & 25 deletions packages/react-admin/src/Admin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import { FunctionComponent } from 'react';
import { AdminProps } from 'ra-core';

import AdminContext from './AdminContext';
@@ -84,30 +83,32 @@ import { useEffect, useState } from 'react';
* );
* };
*/
const Admin: FunctionComponent<AdminProps> = ({
appLayout,
authProvider,
catchAll,
children,
customReducers,
customRoutes = [],
customSagas,
dashboard,
dataProvider,
disableTelemetry,
history,
i18nProvider,
initialState,
layout,
loading,
locale,
loginPage,
logoutButton,
menu, // deprecated, use a custom layout instead
ready,
theme,
title = 'React Admin',
}) => {
const Admin = (props: AdminProps) => {
const {
appLayout,
authProvider,
catchAll,
children,
customReducers,
customRoutes = [],
customSagas,
dashboard,
dataProvider,
disableTelemetry,
history,
i18nProvider,
initialState,
layout,
loading,
locale,
loginPage,
logoutButton,
menu, // deprecated, use a custom layout instead
ready,
theme,
title = 'React Admin',
} = props;

if (appLayout && process.env.NODE_ENV !== 'production') {
console.warn(
'You are using deprecated prop "appLayout", it was replaced by "layout", see https://github.com/marmelab/react-admin/issues/2918'
5 changes: 1 addition & 4 deletions packages/react-admin/src/AdminRouter.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import * as React from 'react';
import { FC } from 'react';
import { CoreAdminRouter, AdminRouterProps } from 'ra-core';
import { LoadingPage } from 'ra-ui-materialui';

const AdminRouter: FC<AdminRouterProps> = props => (
<CoreAdminRouter {...props} />
);
const AdminRouter = (props: AdminRouterProps) => <CoreAdminRouter {...props} />;

AdminRouter.defaultProps = {
loading: LoadingPage,