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,23 +10,24 @@ 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 (
<CalendarBlockContext.Provider <FixedBlockWrapper>
value={{ <CalendarBlockContext.Provider
field, value={{
service, field,
resource, service,
fieldNames, resource,
showLunar, fieldNames,
fixedBlock, showLunar,
}} fixedBlock: field?.decoratorProps?.fixedBlock,
> }}
{props.children} >
</CalendarBlockContext.Provider> {props.children}
</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,20 +34,22 @@ const InternalKanbanBlockProvider = (props) => {
} }
field.loaded = true; field.loaded = true;
return ( return (
<KanbanBlockContext.Provider <FixedBlockWrapper>
value={{ <KanbanBlockContext.Provider
props: { value={{
resource: props.resource, props: {
}, resource: props.resource,
field, },
service, field,
resource, service,
groupField, resource,
fixedBlock, groupField,
}} fixedBlock: field?.decoratorProps?.fixedBlock,
> }}
{props.children} >
</KanbanBlockContext.Provider> {props.children}
</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,24 +24,25 @@ 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 (
<TableBlockContext.Provider <FixedBlockWrapper>
value={{ <TableBlockContext.Provider
field, value={{
service, field,
resource, service,
params, resource,
showIndex, params,
dragSort, showIndex,
rowKey, dragSort,
expandFlag, rowKey,
childrenColumnName, expandFlag,
setExpandFlag: () => setExpandFlag(!expandFlag), childrenColumnName,
}} setExpandFlag: () => setExpandFlag(!expandFlag),
> }}
<RenderChildrenWithAssociationFilter {...props} /> >
</TableBlockContext.Provider> <RenderChildrenWithAssociationFilter {...props} />
</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,56 +90,39 @@ interface FixedBlockProps {
height: number; height: number;
} }
const fixedBlockCss = css`
overflow: hidden;
position: relative;
.noco-card-item {
height: 100%;
.ant-card {
display: flex;
flex-direction: column;
height: 100%;
.ant-card-body {
height: 1px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
}
`;
const FixedBlock: React.FC<FixedBlockProps> = (props) => { const FixedBlock: React.FC<FixedBlockProps> = (props) => {
const { height } = props; const { height } = props;
const [fixedSchema, _setFixedSchema] = useState<Schema>(); const [fixedBlockUID, setFixedBlock] = useState<false | string>(false);
const fixedSchemaRef = useRef(fixedSchema);
const setFixedSchema = (next) => {
if (fixedSchema && next) {
fixedSchemaRef.current = next;
}
_setFixedSchema(next);
};
const schema = useMemo<Schema>(() => {
return fixedSchema?.parent;
}, [fixedSchema]);
return ( return (
<FixedBlockContext.Provider value={{ height, setFixedSchema, fixedSchemaRef }}> <FixedBlockContext.Provider value={{ height, setFixedBlock, fixedBlockUID }}>
{schema ? ( <div
<div className={fixedBlockUID ? fixedBlockCss : ''}
className={css` style={{
overflow: hidden; height: fixedBlockUID ? `calc(100vh - ${height}px)` : undefined,
position: relative; }}
height: calc(100vh - ${height}px); >
.noco-card-item { {props.children}
position: absolute; </div>
top: 0;
left: 0;
right: 0;
bottom: 0;
.ant-card {
display: flex;
flex-direction: column;
height: 100%;
.ant-card-body {
height: 1px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
}
`}
>
<RecursionField onlyRenderProperties={false} name={schema.name} schema={schema} />
</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,23 +11,24 @@ const InternalMapBlockProvider = (props) => {
const { resource, service } = useBlockRequestContext(); const { resource, service } = useBlockRequestContext();
const [selectedRecordKeys, setSelectedRecordKeys] = useState([]); const [selectedRecordKeys, setSelectedRecordKeys] = useState([]);
useFixedSchema();
return ( return (
<SchemaComponentOptions scope={{ selectedRecordKeys }}> <FixedBlockWrapper>
<MapBlockContext.Provider <SchemaComponentOptions scope={{ selectedRecordKeys }}>
value={{ <MapBlockContext.Provider
field, value={{
service, field,
resource, service,
fieldNames, resource,
fixedBlock: fieldSchema?.['x-decorator-props']?.fixedBlock, fieldNames,
selectedRecordKeys, fixedBlock: fieldSchema?.['x-decorator-props']?.fixedBlock,
setSelectedRecordKeys, selectedRecordKeys,
}} setSelectedRecordKeys,
> }}
{props.children} >
</MapBlockContext.Provider> {props.children}
</SchemaComponentOptions> </MapBlockContext.Provider>
</SchemaComponentOptions>
</FixedBlockWrapper>
); );
}; };