Skip to content

Commit 968fde6

Browse files
Merge branch 'main' into rest-data-docs
2 parents 75e9d41 + e0219f1 commit 968fde6

File tree

10 files changed

+84
-18
lines changed

10 files changed

+84
-18
lines changed

.changeset/five-fans-swim.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@refinedev/react-hook-form": patch
3+
---
4+
5+
- Add useEffect to set form values from initial query result when modal is visible.
6+
- Compensates for useForm not setting initial modal form values when querying the same resource id back-to-back.
7+
8+
[Resolves #6904](https://github.com/refinedev/refine/issues/6904)

.changeset/nice-papayas-care.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
"@refinedev/core": patch
3+
---
4+
5+
fix: use stable array to prevent memoization issue in useList. #7019
6+
7+
Fixed an issue where `useList`, `useMany`, `useTable`, and `useCustom` hooks created new empty arrays/objects on every render. This caused `useEffect` and `useMemo` to trigger unnecessarily.
8+
9+
Now these hooks use stable references for better performance.
10+
11+
Fixes #7019

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,8 @@ body:
3434
- type: textarea
3535
attributes:
3636
label: Packages
37-
description: Mention the Refine packages you are using and their versions.
38-
placeholder: |
39-
- @refinedev/antd
40-
- @refinedev/nextjs-router
41-
- @refinedev/simple-rest
37+
description: Run `npm run refine whoami` script and paste the result here.
38+
placeholder: Run `npm run refine whoami` script and paste the result here.
4239
validations:
4340
required: true
4441

documentation/blog/2024-11-27-git-delete.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ image: https://refine.ams3.cdn.digitaloceanspaces.com/blog/2022-11-27-git-delete
88
hide_table_of_contents: false
99
---
1010

11-
**This article was last updated on November 27, 2024, to short explanations for git deleote local and remote branches.**
11+
**This article was last updated on September 26, 2025 to include branch protection policies and team best practices for safe deletion.**
1212

1313
## Introduction
1414

@@ -336,6 +336,14 @@ Similarly if you want to specify the deletion of tags and not branch, then use b
336336
git push origin :refs/tags/tag-name
337337
```
338338

339+
## Branch Protection and Pre-Delete Hooks
340+
341+
To avoid accidental removal of important branches such as `main`, `develop`, or long-lived release lines, modern platforms like GitHub and GitLab offer **branch protection rules**. These rules can block deletion, require pull request reviews, or enforce status checks before changes are merged. Combined with local safeguards, teams can also configure **pre-push or pre-delete Git hooks** that run before destructive actions. For example, a hook could refuse to delete a branch unless a team lead approves, or it could stop pushes that target protected branches. These practices make it harder for human error to remove branches that are still in use or critical to deployment pipelines.
342+
343+
## Best Practices for Teams
344+
345+
When multiple developers are working on the same repository, branch cleanup becomes more than a personal workflow—it’s a team-wide responsibility. A good practice is to schedule periodic reviews of stale branches, ideally at sprint boundaries or release milestones, so old feature branches don’t pile up. Communication also matters: announcing deletions in a team channel helps avoid surprises, especially when others may still be referencing those branches. Tools like `git show-branch`, `git branch --merged`, or GitHub’s branch dashboard can help verify whether a branch is truly inactive before removal. By treating cleanup as part of the collaboration process, teams reduce friction and ensure that only safe, unnecessary branches are deleted.
346+
339347
## Conclusion
340348

341349
In this article, we learned about the different ways to delete a branch in Git also answered frequently asked questions related to deleting a branch in Git.

packages/core/src/hooks/data/useCustom.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export type UseCustomReturnType<TData, TError> = {
114114
};
115115
} & UseLoadingOvertimeReturnType;
116116

117+
const EMPTY_OBJECT = Object.freeze({}) as any;
118+
117119
export const useCustom = <
118120
TQueryFnData extends BaseRecord = BaseRecord,
119121
TError extends HttpError = HttpError,
@@ -231,7 +233,7 @@ export const useCustom = <
231233
return {
232234
query: queryResponse,
233235
result: {
234-
data: queryResponse.data?.data || ({} as TData),
236+
data: queryResponse.data?.data || EMPTY_OBJECT,
235237
},
236238
overtime: { elapsedTime },
237239
};

packages/core/src/hooks/data/useList.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ export type UseListReturnType<TData, TError> = {
101101
};
102102
} & UseLoadingOvertimeReturnType;
103103

104+
const EMPTY_ARRAY = Object.freeze([]) as [];
105+
104106
/**
105107
* `useList` is a modified version of `react-query`'s {@link https://tanstack.com/query/v5/docs/framework/react/guides/queries `useQuery`} used for retrieving items from a `resource` with pagination, sort, and filter configurations.
106108
*
@@ -315,7 +317,7 @@ export const useList = <
315317
return {
316318
query: queryResponse,
317319
result: {
318-
data: queryResponse?.data?.data || [],
320+
data: queryResponse?.data?.data || EMPTY_ARRAY,
319321
total: queryResponse?.data?.total,
320322
},
321323
overtime: { elapsedTime },

packages/core/src/hooks/data/useMany.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ export type UseManyProps<TQueryFnData, TError, TData> = {
8585
LiveModeProps &
8686
UseLoadingOvertimeOptionsProps;
8787

88+
const EMPTY_ARRAY = Object.freeze([]) as [];
89+
8890
/**
8991
* `useMany` is a modified version of `react-query`'s {@link https://tanstack.com/query/v5/docs/framework/react/guides/queries `useQuery`} used for retrieving multiple items from a `resource`.
9092
*
@@ -259,7 +261,7 @@ export const useMany = <
259261
return {
260262
query: queryResponse,
261263
result: {
262-
data: queryResponse?.data?.data || [],
264+
data: queryResponse?.data?.data || EMPTY_ARRAY,
263265
},
264266
overtime: { elapsedTime },
265267
};

packages/core/src/hooks/useTable/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ export type useTableReturnType<
176176

177177
const defaultPermanentFilter: CrudFilter[] = [];
178178
const defaultPermanentSorter: CrudSort[] = [];
179+
const EMPTY_ARRAY = Object.freeze([]) as [];
179180

180181
export function useTable<
181182
TQueryFnData extends BaseRecord = BaseRecord,
@@ -434,7 +435,7 @@ export function useTable<
434435
createLinkForSyncWithLocation,
435436
overtime: queryResult.overtime,
436437
result: {
437-
data: queryResult.result?.data || [],
438+
data: queryResult.result?.data || EMPTY_ARRAY,
438439
total: queryResult.result?.total,
439440
},
440441
};

packages/react-hook-form/src/useModalForm/index.spec.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -291,11 +291,16 @@ describe("useModalForm Hook", () => {
291291

292292
await act(async () => {
293293
result.current.modal.show();
294+
});
295+
296+
await act(async () => {
294297
// register values to form
295298
result.current.register("test");
296299
result.current.setValue("test", "test");
297300
});
298301

302+
expect(result.current.getValues()).toStrictEqual({ test: "test" });
303+
299304
await act(async () => {
300305
result.current.modal.close();
301306
});
@@ -350,20 +355,36 @@ describe("useModalForm Hook", () => {
350355
},
351356
}),
352357
{
353-
wrapper: TestWrapper({}),
358+
wrapper: TestWrapper({
359+
dataProvider: {
360+
...MockJSONServer,
361+
getOne: () =>
362+
Promise.resolve({ data: { id: 5, title: "default-title" } }),
363+
},
364+
}),
354365
},
355366
);
356367

357368
await act(async () => {
358369
result.current.modal.show();
359-
result.current.register("test");
360-
result.current.setValue("test", "test");
370+
});
371+
372+
await act(async () => {
373+
result.current.setValue("title", "new-title");
374+
});
375+
376+
expect(result.current.getValues()).toStrictEqual({
377+
id: 5,
378+
title: "new-title",
361379
});
362380

363381
await act(async () => {
364382
result.current.modal.close();
365383
});
366384

367-
expect(result.current.getValues()).toStrictEqual({});
385+
expect(result.current.getValues()).toStrictEqual({
386+
id: 5,
387+
title: "default-title",
388+
});
368389
});
369390
});

packages/react-hook-form/src/useModalForm/index.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,11 @@ export type UseModalFormProps<
6464
/**
6565
* @description Configuration object for the modal.
6666
* `defaultVisible`: Initial visibility state of the modal.
67-
*
67+
*
6868
* `autoSubmitClose`: Whether the form should be submitted when the modal is closed.
69-
*
69+
*
7070
* `autoResetForm`: Whether the form should be reset when the form is submitted.
71-
*
71+
*
7272
* `autoResetFormWhenClose`: Whether the form should be reset to defaultValues when the modal is closed.
7373
* @type `{
7474
defaultVisible?: boolean;
@@ -176,7 +176,7 @@ export const useModalForm = <
176176

177177
const {
178178
reset,
179-
refineCore: { onFinish, id, setId, autoSaveProps },
179+
refineCore: { onFinish, id, setId, autoSaveProps, query },
180180
saveButtonProps,
181181
handleSubmit,
182182
} = useHookFormResult;
@@ -185,6 +185,20 @@ export const useModalForm = <
185185
defaultVisible,
186186
});
187187

188+
// compensate for setting of initial form values in useForm since it doesnt track modal visibility
189+
React.useEffect(() => {
190+
if (!visible || !query?.data?.data) return;
191+
192+
const formData = query.data.data;
193+
if (!formData) return;
194+
195+
reset(formData as any, {
196+
...(!autoResetFormWhenClose && {
197+
keepDirtyValues: true,
198+
}),
199+
});
200+
}, [visible, query?.data?.data, autoResetFormWhenClose]);
201+
188202
React.useEffect(() => {
189203
if (initiallySynced === false && syncWithLocationKey) {
190204
const openStatus = parsed?.params?.[syncWithLocationKey]?.open;

0 commit comments

Comments
 (0)