diff --git a/packages/ra-core/src/controller/details/useCreateContext.tsx b/packages/ra-core/src/controller/details/useCreateContext.tsx
index e8299188d8f..655afcfd065 100644
--- a/packages/ra-core/src/controller/details/useCreateContext.tsx
+++ b/packages/ra-core/src/controller/details/useCreateContext.tsx
@@ -1,5 +1,5 @@
 import { useContext, useMemo } from 'react';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 
 import { Record } from '../../types';
 import { CreateContext } from './CreateContext';
@@ -35,10 +35,10 @@ export const useCreateContext = <
     // Props take precedence over the context
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
-                props != null ? extractCreateContextProps(props) : {}
+                props != null ? extractCreateContextProps(props) : {},
+                context
             ),
         [context, props]
     );
diff --git a/packages/ra-core/src/controller/details/useEditContext.tsx b/packages/ra-core/src/controller/details/useEditContext.tsx
index b811e7927b3..ad55e753469 100644
--- a/packages/ra-core/src/controller/details/useEditContext.tsx
+++ b/packages/ra-core/src/controller/details/useEditContext.tsx
@@ -1,5 +1,5 @@
 import { useContext, useMemo } from 'react';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 
 import { Record } from '../../types';
 import { EditContext } from './EditContext';
@@ -32,10 +32,10 @@ export const useEditContext = <RecordType extends Record = Record>(
     // Props take precedence over the context
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
-                props != null ? extractEditContextProps(props) : {}
+                props != null ? extractEditContextProps(props) : {},
+                context
             ),
         [context, props]
     );
diff --git a/packages/ra-core/src/controller/details/useShowContext.tsx b/packages/ra-core/src/controller/details/useShowContext.tsx
index a74e3e46365..ef853cbeef7 100644
--- a/packages/ra-core/src/controller/details/useShowContext.tsx
+++ b/packages/ra-core/src/controller/details/useShowContext.tsx
@@ -1,5 +1,5 @@
 import { useContext, useMemo } from 'react';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 
 import { Record } from '../../types';
 import { ShowContext } from './ShowContext';
@@ -32,10 +32,10 @@ export const useShowContext = <RecordType extends Record = Record>(
     // Props take precedence over the context
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
-                props != null ? extractShowContextProps(props) : {}
+                props != null ? extractShowContextProps(props) : {},
+                context
             ),
         [context, props]
     );
diff --git a/packages/ra-core/src/controller/input/useReferenceArrayInputContext.ts b/packages/ra-core/src/controller/input/useReferenceArrayInputContext.ts
index d5f96306b20..4cffb49111b 100644
--- a/packages/ra-core/src/controller/input/useReferenceArrayInputContext.ts
+++ b/packages/ra-core/src/controller/input/useReferenceArrayInputContext.ts
@@ -1,5 +1,5 @@
 import { useContext, useMemo } from 'react';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 import {
     ReferenceArrayInputContext,
     ReferenceArrayInputContextValue,
@@ -17,12 +17,12 @@ export const useReferenceArrayInputContext = <
     // Props take precedence over the context
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
                 props != null
                     ? extractReferenceArrayInputContextProps(props)
-                    : {}
+                    : {},
+                context
             ),
         [context, props]
     );
diff --git a/packages/ra-core/src/controller/useListContext.ts b/packages/ra-core/src/controller/useListContext.ts
index 48609fb749b..67d47ddc215 100644
--- a/packages/ra-core/src/controller/useListContext.ts
+++ b/packages/ra-core/src/controller/useListContext.ts
@@ -1,5 +1,5 @@
 import { useContext, useMemo } from 'react';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 
 import ListContext from './ListContext';
 import { ListControllerProps } from './useListController';
@@ -101,10 +101,10 @@ const useListContext = <RecordType extends Record = Record>(
     // @ts-ignore
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
-                props != null ? extractListContextProps(props) : {}
+                props != null ? extractListContextProps(props) : {},
+                context
             ),
         [context, props]
     );
diff --git a/packages/ra-core/src/controller/useRecordSelection.ts b/packages/ra-core/src/controller/useRecordSelection.ts
index bdb12d3ed29..57dfa2aeb30 100644
--- a/packages/ra-core/src/controller/useRecordSelection.ts
+++ b/packages/ra-core/src/controller/useRecordSelection.ts
@@ -1,4 +1,4 @@
-import { useCallback } from 'react';
+import { useMemo } from 'react';
 import { useSelector, useDispatch, shallowEqual } from 'react-redux';
 import { setListSelectedIds, toggleListItem } from '../actions/listActions';
 import { Identifier, ReduxState } from '../types';
@@ -30,23 +30,20 @@ const useRecordSelection = (
                 : defaultRecords,
         shallowEqual
     );
-    const selectionModifiers = {
-        select: useCallback(
-            (newIds: Identifier[]) => {
+    const selectionModifiers = useMemo(
+        () => ({
+            select: (newIds: Identifier[]) => {
                 dispatch(setListSelectedIds(resource, newIds));
             },
-            [resource] // eslint-disable-line react-hooks/exhaustive-deps
-        ),
-        toggle: useCallback(
-            (id: Identifier) => {
+            toggle: (id: Identifier) => {
                 dispatch(toggleListItem(resource, id));
             },
-            [resource] // eslint-disable-line react-hooks/exhaustive-deps
-        ),
-        clearSelection: useCallback(() => {
-            dispatch(setListSelectedIds(resource, []));
-        }, [resource]), // eslint-disable-line react-hooks/exhaustive-deps
-    };
+            clearSelection: () => {
+                dispatch(setListSelectedIds(resource, []));
+            },
+        }),
+        [dispatch, resource]
+    );
 
     return [selectedIds, selectionModifiers];
 };
diff --git a/packages/ra-core/src/core/useResourceDefinition.ts b/packages/ra-core/src/core/useResourceDefinition.ts
index c204ae1c869..34535073bb4 100644
--- a/packages/ra-core/src/core/useResourceDefinition.ts
+++ b/packages/ra-core/src/core/useResourceDefinition.ts
@@ -1,5 +1,5 @@
 import { useSelector } from 'react-redux';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 import { getResources } from '../reducer';
 import { ResourceDefinition } from '../types';
 import { useResourceContext } from './useResourceContext';
@@ -17,12 +17,16 @@ export const useResourceDefinition = (
 
     const definition = useMemo(() => {
         const definitionFromRedux = resources.find(r => r?.name === resource);
-        return merge({}, definitionFromRedux, {
-            hasCreate,
-            hasEdit,
-            hasList,
-            hasShow,
-        });
+        return defaults(
+            {},
+            {
+                hasCreate,
+                hasEdit,
+                hasList,
+                hasShow,
+            },
+            definitionFromRedux
+        );
     }, [resource, resources, hasCreate, hasEdit, hasList, hasShow]);
 
     return definition;
diff --git a/packages/ra-core/src/dataProvider/useDataProvider.spec.js b/packages/ra-core/src/dataProvider/useDataProvider.spec.js
index b4f4de7f233..0ae8a806d0d 100644
--- a/packages/ra-core/src/dataProvider/useDataProvider.spec.js
+++ b/packages/ra-core/src/dataProvider/useDataProvider.spec.js
@@ -422,7 +422,7 @@ describe('useDataProvider', () => {
                 expect(update).toBeCalledTimes(1);
                 // make sure the side effect hasn't been applied yet
                 expect(queryByText('(updated)')).toBeNull();
-                await act(() => {
+                await act(async () => {
                     resolveUpdate();
                 });
                 // side effects should be applied now
@@ -473,7 +473,7 @@ describe('useDataProvider', () => {
                 // side effects should be applied now
                 expect(queryByText('(updated)')).not.toBeNull();
                 expect(update).toBeCalledTimes(1);
-                await act(() => {
+                act(() => {
                     resolveUpdate();
                 });
             });
@@ -521,7 +521,7 @@ describe('useDataProvider', () => {
                 expect(queryByText('(updated)')).not.toBeNull();
                 // update shouldn't be called at all
                 expect(update).toBeCalledTimes(0);
-                await act(() => {
+                act(() => {
                     undoableEventEmitter.emit('end', {});
                 });
                 expect(update).toBeCalledTimes(1);
diff --git a/packages/ra-ui-materialui/src/form/SimpleFormIterator.spec.tsx b/packages/ra-ui-materialui/src/form/SimpleFormIterator.spec.tsx
index 35b6a9c2009..15c994adc9a 100644
--- a/packages/ra-ui-materialui/src/form/SimpleFormIterator.spec.tsx
+++ b/packages/ra-ui-materialui/src/form/SimpleFormIterator.spec.tsx
@@ -500,7 +500,7 @@ describe('<SimpleFormIterator />', () => {
     });
 
     it('should call the onClick method when the custom add button is clicked', async () => {
-        const onClick = jest.fn();
+        const onClick = jest.fn().mockImplementation(e => e.preventDefault());
         const { getByText } = renderWithRedux(
             <ThemeProvider theme={theme}>
                 <SaveContextProvider value={saveContextValue}>
@@ -527,7 +527,7 @@ describe('<SimpleFormIterator />', () => {
     });
 
     it('should call the onClick method when the custom remove button is clicked', async () => {
-        const onClick = jest.fn();
+        const onClick = jest.fn().mockImplementation(e => e.preventDefault());
         const { getByText } = renderWithRedux(
             <ThemeProvider theme={theme}>
                 <SaveContextProvider value={saveContextValue}>
diff --git a/packages/ra-ui-materialui/src/list/datagrid/useDatagridContext.ts b/packages/ra-ui-materialui/src/list/datagrid/useDatagridContext.ts
index 726843d94c7..efda342d335 100644
--- a/packages/ra-ui-materialui/src/list/datagrid/useDatagridContext.ts
+++ b/packages/ra-ui-materialui/src/list/datagrid/useDatagridContext.ts
@@ -1,7 +1,7 @@
 import { useContext, useMemo } from 'react';
 import { DatagridProps } from './Datagrid';
 import DatagridContext, { DatagridContextValue } from './DatagridContext';
-import merge from 'lodash/merge';
+import defaults from 'lodash/defaults';
 
 export const useDatagridContext = (
     props?: DatagridProps
@@ -10,10 +10,10 @@ export const useDatagridContext = (
 
     return useMemo(
         () =>
-            merge(
+            defaults(
                 {},
-                context,
-                props != null ? { isRowExpandable: props.isRowExpandable } : {}
+                props != null ? { isRowExpandable: props.isRowExpandable } : {},
+                context
             ),
         [context, props]
     );