refactor: improve FixedBlock performance (#1593)

* refactor:  improve FixedBlock performance

* fix: style

* fix: fixedBlock not work when the page have many blocks

* refactor: improve performance
This commit is contained in:
Dunqing 2023-03-29 17:11:20 +08:00 committed by GitHub
parent e6326460b0
commit 6747331529
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 150 deletions

View File

@ -1,7 +1,7 @@
import { ArrayField } from '@formily/core'; import { ArrayField } from '@formily/core';
import { useField } from '@formily/react'; import { useField } from '@formily/react';
import React, { createContext, useContext, useEffect } from 'react'; import React, { createContext, useContext, useEffect } from 'react';
import { useFixedSchema } from '../schema-component'; import { FixedBlockWrapper } from '../schema-component';
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
export const CalendarBlockContext = createContext<any>({}); export const CalendarBlockContext = createContext<any>({});
@ -10,11 +10,11 @@ const InternalCalendarBlockProvider = (props) => {
const { fieldNames, showLunar } = props; const { fieldNames, showLunar } = props;
const field = useField(); const field = useField();
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const fixedBlock = useFixedSchema()
// if (service.loading) { // if (service.loading) {
// return <Spin />; // return <Spin />;
// } // }
return ( return (
<FixedBlockWrapper>
<CalendarBlockContext.Provider <CalendarBlockContext.Provider
value={{ value={{
field, field,
@ -22,11 +22,12 @@ const InternalCalendarBlockProvider = (props) => {
resource, resource,
fieldNames, fieldNames,
showLunar, showLunar,
fixedBlock, fixedBlock: field?.decoratorProps?.fixedBlock,
}} }}
> >
{props.children} {props.children}
</CalendarBlockContext.Provider> </CalendarBlockContext.Provider>
</FixedBlockWrapper>
); );
}; };
@ -53,6 +54,6 @@ export const useCalendarBlockProps = () => {
return { return {
fieldNames: ctx.fieldNames, fieldNames: ctx.fieldNames,
showLunar: ctx.showLunar, showLunar: ctx.showLunar,
fixedBlock: ctx.fixedBlock fixedBlock: ctx.fixedBlock,
}; };
}; };

View File

@ -5,7 +5,7 @@ import uniq from 'lodash/uniq';
import React, { createContext, useContext, useEffect } from 'react'; import React, { createContext, useContext, useEffect } from 'react';
import { useACLRoleContext } from '../acl'; import { useACLRoleContext } from '../acl';
import { useCollection, useCollectionManager } from '../collection-manager'; import { useCollection, useCollectionManager } from '../collection-manager';
import { useFixedSchema } from '../schema-component'; import { FixedBlockWrapper } from '../schema-component';
import { toColumns } from '../schema-component/antd/kanban/Kanban'; import { toColumns } from '../schema-component/antd/kanban/Kanban';
import { BlockProvider, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, useBlockRequestContext } from './BlockProvider';
@ -24,7 +24,6 @@ const useGroupField = (props) => {
const InternalKanbanBlockProvider = (props) => { const InternalKanbanBlockProvider = (props) => {
const field = useField<any>(); const field = useField<any>();
const fixedBlock = useFixedSchema();
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const groupField = useGroupField(props); const groupField = useGroupField(props);
if (!groupField) { if (!groupField) {
@ -35,6 +34,7 @@ const InternalKanbanBlockProvider = (props) => {
} }
field.loaded = true; field.loaded = true;
return ( return (
<FixedBlockWrapper>
<KanbanBlockContext.Provider <KanbanBlockContext.Provider
value={{ value={{
props: { props: {
@ -44,11 +44,12 @@ const InternalKanbanBlockProvider = (props) => {
service, service,
resource, resource,
groupField, groupField,
fixedBlock, fixedBlock: field?.decoratorProps?.fixedBlock,
}} }}
> >
{props.children} {props.children}
</KanbanBlockContext.Provider> </KanbanBlockContext.Provider>
</FixedBlockWrapper>
); );
}; };

View File

@ -3,7 +3,7 @@ import { FormContext, Schema, useField, useFieldSchema } from '@formily/react';
import uniq from 'lodash/uniq'; import uniq from 'lodash/uniq';
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react'; import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { useCollectionManager } from '../collection-manager'; import { useCollectionManager } from '../collection-manager';
import { SchemaComponentOptions, useFixedSchema, removeNullCondition } from '../schema-component'; import { SchemaComponentOptions, removeNullCondition, FixedBlockWrapper } from '../schema-component';
import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider'; import { BlockProvider, RenderChildrenWithAssociationFilter, useBlockRequestContext } from './BlockProvider';
import { useFilterBlock } from '../filter-provider/FilterProvider'; import { useFilterBlock } from '../filter-provider/FilterProvider';
import { findFilterTargets } from './hooks'; import { findFilterTargets } from './hooks';
@ -24,8 +24,8 @@ const InternalTableBlockProvider = (props: Props) => {
const field = useField(); const field = useField();
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const [expandFlag, setExpandFlag] = useState(false); const [expandFlag, setExpandFlag] = useState(false);
useFixedSchema();
return ( return (
<FixedBlockWrapper>
<TableBlockContext.Provider <TableBlockContext.Provider
value={{ value={{
field, field,
@ -42,6 +42,7 @@ const InternalTableBlockProvider = (props: Props) => {
> >
<RenderChildrenWithAssociationFilter {...props} /> <RenderChildrenWithAssociationFilter {...props} />
</TableBlockContext.Provider> </TableBlockContext.Provider>
</FixedBlockWrapper>
); );
}; };

View File

@ -20,6 +20,9 @@ const divWrap = (schema: ISchema) => {
return { return {
type: 'void', type: 'void',
'x-component': 'div', 'x-component': 'div',
'x-component-props': {
className: 'nb-block-wrap',
},
properties: { properties: {
[schema.name || uid()]: schema, [schema.name || uid()]: schema,
}, },

View File

@ -1,51 +1,58 @@
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'; import React, { useContext, useEffect, useMemo, useRef, useState } from 'react';
import { RecursionField, Schema, useField, useFieldSchema } from '@formily/react'; import { useField, useFieldSchema } from '@formily/react';
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import { SchemaSettings } from '../../../schema-settings'; import { SchemaSettings } from '../../../schema-settings';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useDesignable } from '../../hooks'; import { useDesignable } from '../../hooks';
import { useRecord } from '../../../record-provider'; import { useRecord } from '../../../record-provider';
import { useBlockTemplateContext } from '../../../schema-templates/BlockTemplate';
const FixedBlockContext = React.createContext({ const FixedBlockContext = React.createContext<{
setFixedSchema: (schema: Schema) => {}, setFixedBlock: (value: string | false) => void;
height: number;
fixedBlockUID: boolean | string;
}>({
setFixedBlock: () => {},
height: 0, height: 0,
fixedSchemaRef: {} as unknown as React.MutableRefObject<Schema>, fixedBlockUID: false,
}); });
export const useFixedSchema = () => { export const useFixedSchema = () => {
const field = useField(); const field = useField();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const { setFixedSchema, fixedSchemaRef } = useFixedBlock(); const { setFixedBlock, fixedBlockUID } = useFixedBlock();
const { fieldSchema: templateFieldSchema } = useBlockTemplateContext();
const hasSet = useRef(false); const hasSet = useRef(false);
useEffect(() => { useEffect(() => {
if (fieldSchema?.['x-decorator-props']?.fixedBlock) { if (!fixedBlockUID || hasSet.current) {
const nextSchema = templateFieldSchema || fieldSchema; setFixedBlock(field?.decoratorProps?.fixedBlock ? fieldSchema['x-uid'] : false);
setFixedSchema(nextSchema);
hasSet.current = true; hasSet.current = true;
} else if (hasSet.current) {
setFixedSchema(null);
} }
}, [field?.decoratorProps?.fixedBlock, fieldSchema?.['x-decorator-props']?.fixedBlock]); }, [field?.decoratorProps?.fixedBlock]);
useEffect( return fieldSchema['x-uid'] === fixedBlockUID;
() => () => {
if (hasSet.current && fixedSchemaRef.current) {
setFixedSchema(null);
fixedSchemaRef.current = null;
}
},
[],
);
return fieldSchema?.['x-decorator-props']?.fixedBlock
}; };
export const useFixedBlock = () => { export const useFixedBlock = () => {
return useContext(FixedBlockContext); return useContext(FixedBlockContext);
}; };
export const FixedBlockWrapper: React.FC = (props) => {
const fixedBlock = useFixedSchema();
const { height, fixedBlockUID } = useFixedBlock();
// The fixedBlockUID of false means that the page has no fixed blocks
if (!fixedBlock && fixedBlockUID) return null;
return (
<div
className="nb-fixed-block"
style={{
height: fixedBlockUID !== false ? `calc(100vh - ${height}px)` : undefined,
}}
>
{props.children}
</div>
);
};
export const useFixedBlockDesignerSetting = () => { export const useFixedBlockDesignerSetting = () => {
const field = useField(); const field = useField();
const { t } = useTranslation(); const { t } = useTranslation();
@ -83,36 +90,11 @@ interface FixedBlockProps {
height: number; height: number;
} }
const FixedBlock: React.FC<FixedBlockProps> = (props) => { const fixedBlockCss = css`
const { height } = props;
const [fixedSchema, _setFixedSchema] = useState<Schema>();
const fixedSchemaRef = useRef(fixedSchema);
const setFixedSchema = (next) => {
if (fixedSchema && next) {
fixedSchemaRef.current = next;
}
_setFixedSchema(next);
};
const schema = useMemo<Schema>(() => {
return fixedSchema?.parent;
}, [fixedSchema]);
return (
<FixedBlockContext.Provider value={{ height, setFixedSchema, fixedSchemaRef }}>
{schema ? (
<div
className={css`
overflow: hidden; overflow: hidden;
position: relative; position: relative;
height: calc(100vh - ${height}px);
.noco-card-item { .noco-card-item {
position: absolute; height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
.ant-card { .ant-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -126,13 +108,21 @@ const FixedBlock: React.FC<FixedBlockProps> = (props) => {
} }
} }
} }
`} `;
const FixedBlock: React.FC<FixedBlockProps> = (props) => {
const { height } = props;
const [fixedBlockUID, setFixedBlock] = useState<false | string>(false);
return (
<FixedBlockContext.Provider value={{ height, setFixedBlock, fixedBlockUID }}>
<div
className={fixedBlockUID ? fixedBlockCss : ''}
style={{
height: fixedBlockUID ? `calc(100vh - ${height}px)` : undefined,
}}
> >
<RecursionField onlyRenderProperties={false} name={schema.name} schema={schema} /> {props.children}
</div> </div>
) : (
props.children
)}
</FixedBlockContext.Provider> </FixedBlockContext.Provider>
); );
}; };

View File

@ -111,13 +111,22 @@ const pageDesignerCss = css`
} }
`; `;
const pageWithFixedBlockCss = classNames([
'nb-page',
css`
> .nb-grid:not(:last-child) {
> .nb-schema-initializer-button {
display: none;
}
}
`,
]);
export const Page = (props) => { export const Page = (props) => {
const { children, ...others } = props; const { children, ...others } = props;
const field = useField();
const compile = useCompile(); const compile = useCompile();
const { title, setTitle } = useDocumentTitle(); const { title, setTitle } = useDocumentTitle();
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const history = useHistory();
const dn = useDesignable(); const dn = useDesignable();
useEffect(() => { useEffect(() => {
if (!title) { if (!title) {
@ -288,17 +297,7 @@ export const Page = (props) => {
height + 46 + 48 height + 46 + 48
} }
> >
<div <div className={pageWithFixedBlockCss}>{props.children}</div>
className={css`
> .nb-grid:not(:last-child) {
> .nb-schema-initializer-button {
display: none;
}
}
`}
>
{props.children}
</div>
</FixedBlock> </FixedBlock>
)} )}
</div> </div>

View File

@ -1,6 +1,5 @@
import { ArrayField } from '@formily/core';
import { useField, useFieldSchema } from '@formily/react'; import { useField, useFieldSchema } from '@formily/react';
import { BlockProvider, SchemaComponentOptions, useBlockRequestContext, useFixedSchema } from '@nocobase/client'; import { BlockProvider, FixedBlockWrapper, SchemaComponentOptions, useBlockRequestContext } from '@nocobase/client';
import React, { createContext, useContext, useEffect, useState } from 'react'; import React, { createContext, useContext, useEffect, useState } from 'react';
export const MapBlockContext = createContext<any>({}); export const MapBlockContext = createContext<any>({});
@ -12,8 +11,8 @@ const InternalMapBlockProvider = (props) => {
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const [selectedRecordKeys, setSelectedRecordKeys] = useState([]); const [selectedRecordKeys, setSelectedRecordKeys] = useState([]);
useFixedSchema();
return ( return (
<FixedBlockWrapper>
<SchemaComponentOptions scope={{ selectedRecordKeys }}> <SchemaComponentOptions scope={{ selectedRecordKeys }}>
<MapBlockContext.Provider <MapBlockContext.Provider
value={{ value={{
@ -29,6 +28,7 @@ const InternalMapBlockProvider = (props) => {
{props.children} {props.children}
</MapBlockContext.Provider> </MapBlockContext.Provider>
</SchemaComponentOptions> </SchemaComponentOptions>
</FixedBlockWrapper>
); );
}; };