R: Remove redundant useEffect

This commit is contained in:
Ivan 2025-02-05 21:55:26 +03:00
parent 6fbb7fc9aa
commit 2183d659e7
9 changed files with 95 additions and 157 deletions

View File

@ -1,7 +1,7 @@
'use client';
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import { CProps } from '@/components/props';
@ -41,12 +41,13 @@ function PickMultiConstituenta({
className,
...restProps
}: PickMultiConstituentaProps) {
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [filterText, setFilterText] = useState('');
const [filtered, setFiltered] = useState<IConstituenta[]>(items);
const filtered = filterText ? items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)) : items;
const rowSelection: RowSelectionState = Object.fromEntries(
filtered.map((cst, index) => [String(index), value.includes(cst.id)])
);
// TODO: extract graph fold logic to separate function
const foldedGraph = (() => {
if (items.length === schema.items.length) {
return schema.graph;
@ -66,28 +67,6 @@ function PickMultiConstituenta({
return newGraph;
})();
useEffect(() => {
if (filtered.length === 0) {
setRowSelection({});
return;
}
const newRowSelection: RowSelectionState = {};
filtered.forEach((cst, index) => {
newRowSelection[String(index)] = value.includes(cst.id);
});
setRowSelection(newRowSelection);
}, [filtered, setRowSelection, value]);
useEffect(() => {
if (items.length === 0) {
setFiltered([]);
} else if (filterText) {
setFiltered(items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)));
} else {
setFiltered(items);
}
}, [filterText, items]);
function handleRowSelection(updater: React.SetStateAction<RowSelectionState>) {
if (!items) {
onChange([]);

View File

@ -1,5 +1,5 @@
import clsx from 'clsx';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { useIntl } from 'react-intl';
import { IconClose, IconFolderTree } from '@/components/Icons';
@ -45,22 +45,18 @@ function PickSchema({
...restProps
}: PickSchemaProps) {
const intl = useIntl();
const [filterText, setFilterText] = useState(initialFilter);
const [filterLocation, setFilterLocation] = useState('');
const [filtered, setFiltered] = useState<ILibraryItem[]>([]);
const baseFiltered = items.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item)));
const locationMenu = useDropdown();
useEffect(() => {
let newFiltered = baseFiltered.filter(item => matchLibraryItem(item, filterText));
if (filterLocation.length > 0) {
newFiltered = newFiltered.filter(
item => item.location === filterLocation || item.location.startsWith(`${filterLocation}/`)
);
}
setFiltered(newFiltered);
}, [filterText, filterLocation, baseFiltered]);
const [filterText, setFilterText] = useState(initialFilter);
const [filterLocation, setFilterLocation] = useState('');
const baseFiltered = items
.filter(item => item.item_type === itemType && (!baseFilter || baseFilter(item)))
.filter(item => matchLibraryItem(item, filterText));
const filtered =
filterLocation.length > 0
? baseFiltered.filter(item => item.location === filterLocation || item.location.startsWith(`${filterLocation}/`))
: baseFiltered;
const columns = [
columnHelper.accessor('alias', {

View File

@ -1,5 +1,3 @@
import { useEffect, useState } from 'react';
import { CProps } from '@/components/props';
import SelectMulti, { SelectMultiProps } from '@/components/ui/SelectMulti';
import { Grammeme } from '@/models/language';
@ -15,14 +13,10 @@ interface SelectMultiGrammemeProps
}
function SelectMultiGrammeme({ value, onChange, ...restProps }: SelectMultiGrammemeProps) {
const [options, setOptions] = useState<IGrammemeOption[]>([]);
useEffect(() => {
const compatible = getCompatibleGrams(
value.filter(data => Object.values(Grammeme).includes(data.value as Grammeme)).map(data => data.value as Grammeme)
);
setOptions(SelectorGrammemes.filter(({ value }) => compatible.includes(value as Grammeme)));
}, [value]);
const compatible = getCompatibleGrams(
value.filter(data => Object.values(Grammeme).includes(data.value as Grammeme)).map(data => data.value as Grammeme)
);
const options = SelectorGrammemes.filter(({ value }) => compatible.includes(value as Grammeme));
return (
<SelectMulti

View File

@ -3,14 +3,14 @@
*/
import { PARAMETER } from '@/utils/constants';
import { DependencyMode, Position2D } from './miscellaneous';
import { DependencyMode, GraphFilterParams, Position2D } from './miscellaneous';
import { IOperationPosition, IOperationSchema, OperationID, OperationType } from './oss';
import { IConstituenta, IRSForm } from './rsform';
import { ConstituentaID, CstType, IConstituenta, IRSForm } from './rsform';
/**
* Filter list of {@link ILibraryItem} to a given graph query.
*/
export function applyGraphFilter(target: IRSForm, start: number, mode: DependencyMode): IConstituenta[] {
export function applyGraphQuery(target: IRSForm, start: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) {
return target.items;
}
@ -87,3 +87,62 @@ export function calculateInsertPosition(
} while (flagIntersect);
return result;
}
export function produceFilteredGraph(schema: IRSForm, params: GraphFilterParams, focusCst: IConstituenta | undefined) {
const filtered = schema.graph.clone();
const allowedTypes: CstType[] = (() => {
const result: CstType[] = [];
if (params.allowBase) result.push(CstType.BASE);
if (params.allowStruct) result.push(CstType.STRUCTURED);
if (params.allowTerm) result.push(CstType.TERM);
if (params.allowAxiom) result.push(CstType.AXIOM);
if (params.allowFunction) result.push(CstType.FUNCTION);
if (params.allowPredicate) result.push(CstType.PREDICATE);
if (params.allowConstant) result.push(CstType.CONSTANT);
if (params.allowTheorem) result.push(CstType.THEOREM);
return result;
})();
if (params.noHermits) {
filtered.removeIsolated();
}
if (params.noTemplates) {
schema.items.forEach(cst => {
if (cst !== focusCst && cst.is_template) {
filtered.foldNode(cst.id);
}
});
}
if (allowedTypes.length < Object.values(CstType).length) {
schema.items.forEach(cst => {
if (cst !== focusCst && !allowedTypes.includes(cst.cst_type)) {
filtered.foldNode(cst.id);
}
});
}
if (!focusCst && params.foldDerived) {
schema.items.forEach(cst => {
if (cst.spawner) {
filtered.foldNode(cst.id);
}
});
}
if (focusCst) {
const includes: ConstituentaID[] = [
focusCst.id,
...focusCst.spawn,
...(params.focusShowInputs ? schema.graph.expandInputs([focusCst.id]) : []),
...(params.focusShowOutputs ? schema.graph.expandOutputs([focusCst.id]) : [])
];
schema.items.forEach(cst => {
if (!includes.includes(cst.id)) {
filtered.foldNode(cst.id);
}
});
}
if (params.noTransitive) {
filtered.transitiveReduction();
}
return filtered;
}

View File

@ -49,6 +49,7 @@ function FormConstituenta({ disabled, id, toggleReset, schema, activeCst, onOpen
} = useForm<ICstUpdateDTO>({ resolver: zodResolver(CstUpdateSchema) });
const [localParse, setLocalParse] = useState<IExpressionParse | undefined>(undefined);
const typification = localParse
? labelTypification({
isValid: localParse.parseResult,

View File

@ -1,7 +1,7 @@
'use client';
import fileDownload from 'js-file-download';
import { useEffect, useState } from 'react';
import { useState } from 'react';
import { toast } from 'react-toastify';
import { useMutatingRSForm } from '@/backend/rsform/useMutatingRSForm';
@ -11,7 +11,7 @@ import MiniButton from '@/components/ui/MiniButton';
import Overlay from '@/components/ui/Overlay';
import SearchBar from '@/components/ui/SearchBar';
import { CstMatchMode } from '@/models/miscellaneous';
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
import { ConstituentaID, CstType } from '@/models/rsform';
import { matchConstituenta } from '@/models/rsformAPI';
import { useFitHeight } from '@/stores/appLayout';
import { information } from '@/utils/labels';
@ -22,34 +22,18 @@ import TableRSList from './TableRSList';
import ToolbarRSList from './ToolbarRSList';
function EditorRSList() {
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const controller = useRSEdit();
const isProcessing = useMutatingRSForm();
const [filtered, setFiltered] = useState<IConstituenta[]>(controller.schema.items);
const [filterText, setFilterText] = useState('');
useEffect(() => {
if (filtered.length === 0) {
setRowSelection({});
return;
}
const newRowSelection: RowSelectionState = {};
filtered.forEach((cst, index) => {
newRowSelection[String(index)] = controller.selected.includes(cst.id);
});
setRowSelection(newRowSelection);
}, [filtered, setRowSelection, controller.selected]);
const filtered = filterText
? controller.schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL))
: controller.schema.items;
useEffect(() => {
if (controller.schema.items.length === 0) {
setFiltered([]);
} else if (filterText) {
setFiltered(controller.schema.items.filter(cst => matchConstituenta(cst, filterText, CstMatchMode.ALL)));
} else {
setFiltered(controller.schema.items);
}
}, [filterText, controller.schema.items]);
const rowSelection: RowSelectionState = Object.fromEntries(
filtered.map((cst, index) => [String(index), controller.selected.includes(cst.id)])
);
function handleDownloadCSV() {
if (filtered.length === 0) {

View File

@ -25,6 +25,7 @@ import SelectedCounter from '@/components/info/SelectedCounter';
import { CProps } from '@/components/props';
import ToolbarGraphSelection from '@/components/select/ToolbarGraphSelection';
import Overlay from '@/components/ui/Overlay';
import { produceFilteredGraph } from '@/models/miscellaneousAPI';
import { ConstituentaID, CstType, IConstituenta } from '@/models/rsform';
import { isBasicConcept } from '@/models/rsformAPI';
import { useMainHeight } from '@/stores/appLayout';
@ -42,7 +43,6 @@ import { TGNodeTypes } from './graph/TGNodeTypes';
import GraphSelectors from './GraphSelectors';
import ToolbarFocusedCst from './ToolbarFocusedCst';
import ToolbarTermGraph from './ToolbarTermGraph';
import useGraphFilter from './useGraphFilter';
import ViewHidden from './ViewHidden';
const ZOOM_MAX = 3;
@ -67,7 +67,7 @@ function TGFlow() {
const [edges, setEdges] = useEdgesState([]);
const [focusCst, setFocusCst] = useState<IConstituenta | undefined>(undefined);
const filteredGraph = useGraphFilter(controller.schema, filter, focusCst);
const filteredGraph = produceFilteredGraph(controller.schema, filter, focusCst);
const [hidden, setHidden] = useState<ConstituentaID[]>([]);
const [isDragging, setIsDragging] = useState(false);

View File

@ -1,75 +0,0 @@
import { useEffect, useState } from 'react';
import { Graph } from '@/models/Graph';
import { GraphFilterParams } from '@/models/miscellaneous';
import { ConstituentaID, CstType, IConstituenta, IRSForm } from '@/models/rsform';
function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, focusCst: IConstituenta | undefined) {
const [filtered, setFiltered] = useState<Graph>(new Graph());
const allowedTypes: CstType[] = (() => {
const result: CstType[] = [];
if (params.allowBase) result.push(CstType.BASE);
if (params.allowStruct) result.push(CstType.STRUCTURED);
if (params.allowTerm) result.push(CstType.TERM);
if (params.allowAxiom) result.push(CstType.AXIOM);
if (params.allowFunction) result.push(CstType.FUNCTION);
if (params.allowPredicate) result.push(CstType.PREDICATE);
if (params.allowConstant) result.push(CstType.CONSTANT);
if (params.allowTheorem) result.push(CstType.THEOREM);
return result;
})();
useEffect(() => {
if (!schema) {
setFiltered(new Graph());
return;
}
const graph = schema.graph.clone();
if (params.noHermits) {
graph.removeIsolated();
}
if (params.noTemplates) {
schema.items.forEach(cst => {
if (cst !== focusCst && cst.is_template) {
graph.foldNode(cst.id);
}
});
}
if (allowedTypes.length < Object.values(CstType).length) {
schema.items.forEach(cst => {
if (cst !== focusCst && !allowedTypes.includes(cst.cst_type)) {
graph.foldNode(cst.id);
}
});
}
if (!focusCst && params.foldDerived) {
schema.items.forEach(cst => {
if (cst.spawner) {
graph.foldNode(cst.id);
}
});
}
if (focusCst) {
const includes: ConstituentaID[] = [
focusCst.id,
...focusCst.spawn,
...(params.focusShowInputs ? schema.graph.expandInputs([focusCst.id]) : []),
...(params.focusShowOutputs ? schema.graph.expandOutputs([focusCst.id]) : [])
];
schema.items.forEach(cst => {
if (!includes.includes(cst.id)) {
graph.foldNode(cst.id);
}
});
}
if (params.noTransitive) {
graph.transitiveReduction();
}
setFiltered(graph);
}, [schema, params, allowedTypes, focusCst]);
return filtered;
}
export default useGraphFilter;

View File

@ -7,7 +7,7 @@ import SelectGraphFilter from '@/components/select/SelectGraphFilter';
import SelectMatchMode from '@/components/select/SelectMatchMode';
import MiniButton from '@/components/ui/MiniButton';
import SearchBar from '@/components/ui/SearchBar';
import { applyGraphFilter } from '@/models/miscellaneousAPI';
import { applyGraphQuery } from '@/models/miscellaneousAPI';
import { ConstituentaID, IConstituenta, IRSForm } from '@/models/rsform';
import { matchConstituenta } from '@/models/rsformAPI';
import { useCstSearchStore } from '@/stores/cstSearch';
@ -40,7 +40,7 @@ function ConstituentsSearch({ schema, activeID, activeExpression, dense, onChang
if (!activeID) {
result = schema.items;
} else {
result = applyGraphFilter(schema, activeID, filterSource);
result = applyGraphQuery(schema, activeID, filterSource);
}
if (query) {
result = result.filter(cst => matchConstituenta(cst, query, filterMatch));