diff --git a/app/screens/home/search/search.test.tsx b/app/screens/home/search/search.test.tsx
index f88a3adf4..7a1dac3b3 100644
--- a/app/screens/home/search/search.test.tsx
+++ b/app/screens/home/search/search.test.tsx
@@ -75,6 +75,10 @@ describe('SearchScreen', () => {
jest.clearAllMocks();
});
+ afterEach(() => {
+ jest.restoreAllMocks();
+ });
+
it('renders search screen correctly', () => {
const {getByTestId, getByText, getByPlaceholderText} = renderWithEverything(
,
@@ -238,6 +242,54 @@ describe('SearchScreen', () => {
});
});
+ it('should not trigger a duplicate search via useDidUpdate when refocused with a new searchTerm', async () => {
+ let currentIsFocused = false;
+ let currentSearchTerm = '#old';
+
+ jest.spyOn(require('@react-navigation/native'), 'useIsFocused').mockImplementation(() => currentIsFocused);
+ jest.spyOn(require('@react-navigation/native'), 'useNavigation').mockImplementation(() => ({
+ getState: () => ({
+ index: 0,
+ routes: [{params: {searchTerm: currentSearchTerm}}],
+ }),
+ }));
+
+ const {rerender} = renderWithEverything(, {database});
+
+ // Initial focus with #old triggers useEffect search
+ await act(async () => {
+ currentIsFocused = true;
+ rerender();
+ });
+
+ await waitFor(() => expect(searchPosts).toHaveBeenCalledWith(
+ expect.any(String), 'team1', expect.objectContaining({terms: '#old'}),
+ ));
+
+ // Navigate away
+ await act(async () => {
+ currentIsFocused = false;
+ rerender();
+ });
+
+ jest.clearAllMocks();
+
+ // Navigate back with a new hashtag — useDidUpdate and useEffect both fire,
+ // but useDidUpdate guard should skip since searchTerm !== lastSearchedValue
+ await act(async () => {
+ currentSearchTerm = '#new';
+ currentIsFocused = true;
+ rerender();
+ });
+
+ await waitFor(() => {
+ expect(searchPosts).toHaveBeenCalledTimes(1);
+ expect(searchPosts).toHaveBeenCalledWith(
+ expect.any(String), 'team1', expect.objectContaining({terms: '#new'}),
+ );
+ });
+ });
+
it('populates searchTerm when navigating to screen with hashtag', async () => {
const mockNavigation = {
getState: () => ({
diff --git a/app/screens/home/search/search.tsx b/app/screens/home/search/search.tsx
index ddb707323..63efdcdca 100644
--- a/app/screens/home/search/search.tsx
+++ b/app/screens/home/search/search.tsx
@@ -398,6 +398,11 @@ const SearchScreen = ({teamId, teams, crossTeamSearchEnabled}: Props) => {
}, [isFocused]);
useDidUpdate(() => {
+ if (searchTerm && searchTerm !== lastSearchedValue) {
+ // searchTerm changed (case for hashtag tap) — useEffect handles it; skip to avoid double search
+ return;
+ }
+
if (isFocused && lastSearchedValue && showResults) {
// requestAnimationFrame for smooth UI updates
requestAnimationFrame(() => {