Fix "PageChangeEffect does not run when changing view on the same object" (#12196)

Fixes https://github.com/twentyhq/core-team-issues/issues/950

This issue was due to the memoization inside `useIsMatchingLocation`,
which was rerendered only if the pathname changed but not the search
params.

After discussion with @lucasbordeau, we decided to remove the hook
`useIsMatchingLocation` and to create an equivalent util function which
takes the location as an argument.

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Raphaël Bosi
2025-05-22 12:06:07 +02:00
committed by GitHub
parent 6466f3fb45
commit ffdedf7af3
16 changed files with 232 additions and 195 deletions

View File

@ -0,0 +1,89 @@
import { AppBasePath } from '@/types/AppBasePath';
import { isMatchingLocation } from '../isMatchingLocation';
describe('isMatchingLocation', () => {
it('returns true when paths match with no basePath', () => {
const location = {
pathname: '/example',
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(location, '/example');
expect(result).toBe(true);
});
it('returns false when paths do not match with no basePath', () => {
const location = {
pathname: '/example',
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(location, '/non-match');
expect(result).toBe(false);
});
it('returns true when paths match with basePath', () => {
const location = {
pathname: `${AppBasePath.Settings}/example`,
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(
location,
'example',
AppBasePath.Settings,
);
expect(result).toBe(true);
});
it('returns false when paths do not match with basePath', () => {
const location = {
pathname: `${AppBasePath.Settings}/example`,
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(
location,
'non-match',
AppBasePath.Settings,
);
expect(result).toBe(false);
});
it('handles trailing slashes in basePath correctly', () => {
const location = {
pathname: `${AppBasePath.Settings}/example`,
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(
location,
'example',
(AppBasePath.Settings + '/') as AppBasePath,
);
expect(result).toBe(true);
});
it('handles paths without basePath correctly', () => {
const location = {
pathname: '/example',
state: null,
key: 'test-key',
search: '',
hash: '',
};
const result = isMatchingLocation(location, '/example');
expect(result).toBe(true);
});
});

View File

@ -0,0 +1,26 @@
import { Location, matchPath } from 'react-router-dom';
import { AppBasePath } from '@/types/AppBasePath';
import { isNonEmptyString } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
const addTrailingSlash = (path: string) =>
path.endsWith('/') ? path : path + '/';
const getConstructedPath = (path: string, basePath?: AppBasePath) => {
if (!isNonEmptyString(basePath)) return path;
return addTrailingSlash(basePath) + path;
};
export const isMatchingLocation = (
location: Location,
path: string,
basePath?: AppBasePath,
): boolean => {
const match = matchPath(
getConstructedPath(path, basePath),
location.pathname,
);
return isDefined(match);
};