Skip to content

Commit f6995d8

Browse files
Avoid additional rerendering using callbacks and refs
1 parent 86c1b31 commit f6995d8

File tree

6 files changed

+82
-49
lines changed

6 files changed

+82
-49
lines changed

src/components/models-download/ModelsDownload.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ export default function ModelsDownload() {
5959
const orgId = useSelector(selectOrganizationId);
6060
useWebsocket(orgId, Application.REFINERY, CurrentPage.MODELS_DOWNLOAD, handleWebsocketNotification);
6161

62+
const openCreateModal = useCallback(() => {
63+
dispatch(openModal(ModalEnum.ADD_MODEL_DOWNLOAD));
64+
}, []);
65+
6266
return (<div className="p-4 bg-gray-100 flex-1 flex flex-col h-[calc(100vh-4rem)] overflow-y-auto">
6367
<div className="flex flex-row items-center">
6468
<ButtonAsText
@@ -87,7 +91,7 @@ export default function ModelsDownload() {
8791
<KernButton
8892
text="Add new model"
8993
icon={IconPlus}
90-
onClick={() => dispatch(openModal(ModalEnum.ADD_MODEL_DOWNLOAD))}
94+
onClick={openCreateModal}
9195
/>
9296
</div>
9397
</div>

src/components/projects/ButtonsContainer.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,31 @@ export default function ButtonsContainer() {
3737
const orgId = useSelector(selectOrganizationId);
3838
useWebsocket(orgId, Application.REFINERY, CurrentPage.PROJECTS, handleWebsocketNotification, null, CurrentPageSubKey.BUTTONS_CONTAINER);
3939

40+
const createNewProject = useCallback(() => {
41+
dispatch(setUploadFileType(UploadFileType.RECORDS_NEW));
42+
router.push("/projects/new");
43+
}, []);
44+
45+
const importSnapshot = useCallback(() => {
46+
dispatch(openModal(ModalEnum.MODAL_UPLOAD));
47+
dispatch(setUploadFileType(UploadFileType.PROJECT));
48+
}, []);
49+
4050
return (
4151
user && user.role === UserRole.ENGINEER ? (<div className="flex flex-row items-start gap-x-4 mt-4">
4252
<KernButton
4353
text='New project'
4454
buttonColor="blue"
4555
solidTheme={true}
4656
textColor="white"
47-
onClick={() => {
48-
dispatch(setUploadFileType(UploadFileType.RECORDS_NEW));
49-
router.push("/projects/new");
50-
}}
57+
onClick={createNewProject}
5158
/>
5259
<KernButton
5360
text="Import snapshot"
5461
buttonColor="blue"
5562
solidTheme={true}
5663
textColor="white"
57-
onClick={() => {
58-
dispatch(openModal(ModalEnum.MODAL_UPLOAD));
59-
dispatch(setUploadFileType(UploadFileType.PROJECT));
60-
}}
64+
onClick={importSnapshot}
6165
/>
6266
<SampleProjectsDropdown />
6367
<ModalUpload uploadOptions={uploadOptions} />

src/components/projects/ProjectCard.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { TOOLTIPS_DICT } from "@/src/util/tooltip-constants";
1313
import { deleteProjectPost } from "@/src/services/base/project";
1414
import ButtonAsText from "@/submodules/react-components/components/kern-button/ButtonAsText";
1515
import { MemoIconX } from "@/submodules/react-components/components/kern-icons/icons";
16+
import { useCallback } from "react";
17+
import useRefFor from "@/submodules/react-components/hooks/useRefFor";
1618

1719
export default function ProjectCard(props: ProjectCardProps) {
1820
const router = useRouter();
@@ -21,20 +23,6 @@ export default function ProjectCard(props: ProjectCardProps) {
2123
const isAdmin = useSelector(selectIsAdmin);
2224
const user = useSelector(selectUser);
2325

24-
function adminOpenOrDeleteProject(project: Project) {
25-
if (!isAdmin) return;
26-
const deleteInstant = isStringTrue(localStorage.getItem("adminInstantDelete"));
27-
if (deleteInstant) {
28-
deleteProjectPost(project.id, (res) => {
29-
dispatch(closeModal(ModalEnum.ADMIN_DELETE_PROJECT));
30-
dispatch(removeFromAllProjectsById(project.id));
31-
})
32-
}
33-
else {
34-
dispatch(setModalStates(ModalEnum.ADMIN_DELETE_PROJECT, { projectId: project.id, open: true }));
35-
}
36-
}
37-
3826
function manageProject(): void {
3927
const projectId = props.project.id;
4028
if (user?.role == 'ENGINEER') {
@@ -48,6 +36,21 @@ export default function ProjectCard(props: ProjectCardProps) {
4836
}
4937
}
5038

39+
const projectId = useRefFor(props.project.id);
40+
const adminOpenOrDeleteProjectFunc = useCallback(() => {
41+
if (!isAdmin) return;
42+
const deleteInstant = isStringTrue(localStorage.getItem("adminInstantDelete"));
43+
if (deleteInstant) {
44+
deleteProjectPost(projectId.current, (res) => {
45+
dispatch(closeModal(ModalEnum.ADMIN_DELETE_PROJECT));
46+
dispatch(removeFromAllProjectsById(projectId.current));
47+
})
48+
}
49+
else {
50+
dispatch(setModalStates(ModalEnum.ADMIN_DELETE_PROJECT, { projectId: projectId.current, open: true }));
51+
}
52+
}, []);
53+
5154
return (
5255
<div key={props.project.id} className="relative card shadow bg-white m-4 rounded-2xl">
5356
{(props.project.status != ProjectStatus.IN_DELETION && props.project.status != ProjectStatus.HIDDEN) && (
@@ -65,7 +68,7 @@ export default function ProjectCard(props: ProjectCardProps) {
6568
</>}
6669
</div>}
6770
{(isAdmin && props.project.status !== ProjectStatus.INIT_SAMPLE_PROJECT) &&
68-
<div className="absolute top-0 left-0 cursor-pointer" onClick={() => adminOpenOrDeleteProject(props.project)}>
71+
<div className="absolute top-0 left-0 cursor-pointer" onClick={adminOpenOrDeleteProjectFunc}>
6972
<Tooltip content={TOOLTIPS_DICT.PROJECTS.QUICK_DELETE} color="invert" offset={2} placement="right">
7073
<MemoIconX className="h-6 w-6 text-gray-500" />
7174
</Tooltip>

src/components/projects/SampleProjectsDropdown.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ export default function SampleProjectsDropdown() {
7373
}
7474
}, []);
7575

76+
const furtherSampleProjects = useCallback(() => {
77+
window.open("https://github.com/code-kern-ai/refinery-sample-projects", "_blank")
78+
}, []);
79+
7680
return (
7781
<Menu as="div" className="relative inline-block text-left">
7882
<div>
@@ -212,7 +216,7 @@ export default function SampleProjectsDropdown() {
212216
{({ active }) => (
213217
<a key="sample-project-6"
214218
className={`opacity-100 cursor-pointer text-gray-900 block px-3 py-2 text-sm ${active ? "bg-kernindigo text-white" : ""}`}
215-
onClick={() => window.open("https://github.com/code-kern-ai/refinery-sample-projects", "_blank")}>
219+
onClick={furtherSampleProjects}>
216220
<span>Further sample projects</span>
217221
</a>
218222
)}

src/components/projects/projectId/attributes/attributeId/AttributeCalculations.tsx

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -198,49 +198,48 @@ export default function AttributeCalculation() {
198198

199199
}
200200

201-
function changeAttributeName(name: string) {
202-
if (name == currentAttribute.name) return;
201+
const changeAttributeName = useCallback((name: string) => {
202+
if (name == currentAttributeRef.current.name) return;
203203
if (name == '') return;
204204
const duplicateNameExists = attributes.find((attribute) => attribute.name == name);
205205
if (duplicateNameExists) {
206206
setDuplicateNameExists(true);
207-
setAttributeName(currentAttribute.name);
207+
setAttributeName(currentAttributeRef.current.name);
208208
return;
209209
}
210-
const attributeNew = { ...currentAttribute };
210+
const attributeNew = { ...currentAttributeRef.current };
211211
attributeNew.name = name;
212212
attributeNew.saveSourceCode = false;
213-
updateAttribute(projectId, currentAttribute.id, (res) => {
213+
updateAttribute(projectId, currentAttributeRef.current.id, (res) => {
214214
setCurrentAttribute(postProcessCurrentAttribute(attributeNew));
215215
setEditorValue(attributeNew.sourceCode.replace('def ac(record)', 'def ' + attributeNew.name + '(record)'));
216216
dispatch(updateAttributeById(attributeNew));
217217
setDuplicateNameExists(false);
218218
}, null, null, attributeNew.name);
219-
}
219+
}, []);
220220

221-
function updateVisibility(option: any) {
222-
const attributeNew = { ...currentAttribute };
221+
const updateVisibility = useCallback((option: any) => {
222+
const attributeNew = { ...currentAttributeRef.current };
223223
attributeNew.visibility = option.value;
224224
attributeNew.visibilityIndex = ATTRIBUTES_VISIBILITY_STATES.findIndex((state) => state.name === option);
225225
attributeNew.visibilityName = option.name;
226226
attributeNew.saveSourceCode = false;
227-
updateAttribute(projectId, currentAttribute.id, (res) => {
227+
updateAttribute(projectId, currentAttributeRef.current.id, (res) => {
228228
setCurrentAttribute(postProcessCurrentAttribute(attributeNew));
229229
dispatch(updateAttributeById(attributeNew));
230230
}, null, null, null, null, attributeNew.visibility);
231-
}
231+
}, []);
232232

233-
function updateDataType(option: any) {
234-
const attributeNew = { ...currentAttribute };
233+
const updateDataType = useCallback((option: any) => {
234+
const attributeNew = { ...currentAttributeRef.current };
235235
attributeNew.dataType = option.value;
236236
attributeNew.dataTypeName = option.name;
237237
attributeNew.saveSourceCode = false;
238-
updateAttribute(projectId, currentAttribute.id, (res) => {
238+
updateAttribute(projectId, currentAttributeRef.current.id, (res) => {
239239
setCurrentAttribute(postProcessCurrentAttribute(attributeNew));
240240
dispatch(updateAttributeById(attributeNew));
241241
}, attributeNew.dataType);
242-
}
243-
242+
}, []);
244243

245244
function onScrollEvent(event: any) {
246245
if (!(event.target instanceof HTMLElement)) return;
@@ -251,7 +250,6 @@ export default function AttributeCalculation() {
251250
}
252251
}
253252

254-
255253
function checkProjectTokenization() {
256254
getProjectTokenization(projectId, (res) => {
257255
setTokenizationProgress(res?.progress);
@@ -311,6 +309,16 @@ export default function AttributeCalculation() {
311309
const orgId = useSelector(selectOrganizationId);
312310
useWebsocket(orgId, Application.REFINERY, CurrentPage.ATTRIBUTE_CALCULATION, handleWebsocketNotification, projectId);
313311

312+
const goBack = useCallback((e: React.MouseEvent) => {
313+
e.preventDefault();
314+
router.push(`/projects/${projectId}/settings`);
315+
}, []);
316+
317+
const copyToClipboardFunc = useCallback(
318+
(name: string) => () => copyToClipboard(name),
319+
[]
320+
);
321+
314322
const disabledOptions = useMemo(() => {
315323
if (!currentAttribute || currentAttribute.dataType == DataTypeEnum.LLM_RESPONSE) return undefined;
316324
return DATA_TYPES.map((e) => e.value == DataTypeEnum.LLM_RESPONSE);
@@ -321,10 +329,7 @@ export default function AttributeCalculation() {
321329
<div className={`sticky z-50 h-12 ${isHeaderNormal ? 'top-1' : '-top-5'}`}>
322330
<div className={`bg-white flex-grow ${isHeaderNormal ? '' : 'shadow'}`}>
323331
<div className={`flex-row justify-start items-center inline-block ${isHeaderNormal ? 'p-0' : 'flex py-2'}`} style={{ transition: 'all .25s ease-in-out' }}>
324-
<a href={`/refinery/projects/${projectId}/settings`} onClick={(e) => {
325-
e.preventDefault();
326-
router.push(`/projects/${projectId}/settings`);
327-
}} className="text-green-800 text-sm font-medium">
332+
<a href={`/refinery/projects/${projectId}/settings`} onClick={goBack} className="text-green-800 text-sm font-medium">
328333
<MemoIconArrowLeft className="h-5 w-5 inline-block text-green-800" />
329334
<span className="leading-5">Go back</span>
330335
</a>
@@ -386,7 +391,7 @@ export default function AttributeCalculation() {
386391
{usableAttributes.length == 0 && <div className="text-sm font-normal text-gray-500">No usable attributes.</div>}
387392
{usableAttributes.map((attribute: Attribute) => (
388393
<Tooltip key={attribute.id} content={attribute.dataTypeName + ' - ' + TOOLTIPS_DICT.GENERAL.CLICK_TO_COPY} color="invert" placement="top">
389-
<span onClick={() => copyToClipboard(attribute.name)}>
394+
<span onClick={copyToClipboardFunc(attribute.name)}>
390395
<div className={`cursor-pointer border items-center px-2 py-0.5 rounded text-xs font-medium text-center mr-2 ${'bg-' + attribute.color + '-100'} ${'text-' + attribute.color + '-700'} ${'border-' + attribute.color + '-400'} ${'hover:bg-' + attribute.color + '-200'}`}>
391396
{attribute.name}
392397
</div>
@@ -400,7 +405,7 @@ export default function AttributeCalculation() {
400405
<div className="flex flex-row items-center">
401406
{lookupLists.map((lookupList) => (
402407
<Tooltip key={lookupList.id} content={TOOLTIPS_DICT.GENERAL.IMPORT_STATEMENT} color="invert" placement="top">
403-
<span onClick={() => copyToClipboard("from knowledge import " + lookupList.pythonVariable)}>
408+
<span onClick={copyToClipboardFunc("from knowledge import " + lookupList.pythonVariable)}>
404409
<div className="cursor-pointer border items-center px-2 py-0.5 rounded text-xs font-medium text-center mr-2">
405410
{lookupList.pythonVariable} - {lookupList.termCount}
406411
</div>

src/components/projects/projectId/data-browser/DataSliceOperations.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import CreateOutlierSliceModal from "./modals/CreateOutlierSliceModal";
1414
import SaveDataSliceModal from "./modals/SaveDataSliceModal";
1515
import { createOutlierSlice, updateDataSlice } from "@/src/services/base/data-browser";
1616
import KernButton from "@/submodules/react-components/components/kern-button/KernButton";
17+
import { useCallback } from "react";
1718

1819
export function DataSliceOperations(props: { fullSearch: {} }) {
1920
const dispatch = useDispatch();
@@ -29,7 +30,19 @@ export function DataSliceOperations(props: { fullSearch: {} }) {
2930
const additionalData = useSelector(selectAdditionalData);
3031
const embeddings = useSelector(selectEmbeddings);
3132

32-
function updateSlice() {
33+
// function updateSlice() {
34+
// updateDataSlice(projectId, {
35+
// static: activeSlice.static,
36+
// dataSliceId: activeSlice.id,
37+
// filterRaw: getRawFilterForSave(props.fullSearch),
38+
// filterData: parseFilterToExtended(activeSearchParams, attributes, configuration, labelingTasks, user, props.fullSearch[SearchGroup.DRILL_DOWN].value)
39+
// }, (res) => {
40+
// dispatch(setActiveDataSlice(activeSlice));
41+
// });
42+
// dispatch(updateAdditionalDataState('displayOutdatedWarning', false));
43+
// }
44+
45+
const updateSlice = useCallback(() => {
3346
updateDataSlice(projectId, {
3447
static: activeSlice.static,
3548
dataSliceId: activeSlice.id,
@@ -39,7 +52,7 @@ export function DataSliceOperations(props: { fullSearch: {} }) {
3952
dispatch(setActiveDataSlice(activeSlice));
4053
});
4154
dispatch(updateAdditionalDataState('displayOutdatedWarning', false));
42-
}
55+
}, [activeSlice, projectId, props.fullSearch, dispatch, activeSearchParams, attributes, configuration, labelingTasks, user]);
4356

4457
function requestOutlierSlice() {
4558
if (embeddings.length == 0) return;

0 commit comments

Comments
 (0)