Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(fix) O3-4382: Fixing the reset button functionality #2217

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

UNCANNY69
Copy link

Requirements

  • This PR has a title that briefly describes the work done including the ticket number. If there is a ticket, make sure your PR title includes a conventional commit label. See existing PR titles for inspiration.
  • My work conforms to the OpenMRS 3.0 Styleguide and design documentation.
  • My work includes tests or is validated by existing tests.

Summary

In the initial implementation, the FilterProvider component was being re-created whenever the parent component (TreeViewWrapper) re-rendered. This caused the state within the FilterProvider to reset, even when no changes were made to its inputs. The observed flow of console.log statements showed that the FilterProvider was being re-rendered unnecessarily, leading to unintended side effects like resetting the checkbox values stored in the filter state.

The root cause was that the FilterProvider component's re-creation was linked to the lifecycle of TreeViewWrapper, which was itself re-rendering due to changes in its own state or props.

Solution:
Initially it led me to think roots were needed to be memoized the roots , but for the life of me I am not sure why it did not work thus I used React.memo as it memoizes the rendered output of the FilterProvider and skips re-renders unless its props change. By memoizing it, the FilterProvider remains the same instance across renders unless the roots prop is updated.

Screenshots

OpenMRS.-.Brave.2025-01-29.02-32-21.mp4

Related Issue

Other

@denniskigen denniskigen requested a review from ibacher January 28, 2025 21:58
Copy link
Member

@denniskigen denniskigen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a stab at this, @UNCANNY69. While the memoization suggestion helps, the core issue appears to be deeper within the FilterContext flow. The RESET_TREE action in the FilterContext reducer fires correctly but fails to clear the checkboxes. There seems to be an issue with the FilterProvider state initialization that prevents re-initialization when the roots change.

To test this hypothesis out, try adding this force reset to FilterProvider that fires when roots changes:

   useEffect(() => {
     dispatch({ type: ReducerActionType.RESET_TREE });
   }, [roots]);

You'll see that this resets the filters. There's still some exploration to be done, but I think it's better to focus there for the moment.

@@ -24,12 +24,12 @@ const TreeViewWrapper: React.FC<TreeViewWrapperProps> = (props) => {
const { roots, isLoading, error } = useGetManyObstreeData(conceptUuids);

if (error) return <ErrorState error={error} headerTitle={t('dataLoadError', 'Data load error')} />;

const MemoizedFilterProvider = React.memo(FilterProvider);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because MemoizedFilterProvider is declared within the TreeViewWrapper component:

  • A new component type is created on every render of TreeViewWrapper
  • React will unmount the older provider and mount a new one each time TreeViewWrapper re-renders
  • Any state in the subtree of FilterProvider will reset on re-render

We should move the memoized component outside the parent to preserve its identity as a stable reference:

const MemoizedFilterProvider = React.memo(FilterProvider); 

const TreeViewWrapper: React.FC<TreeViewWrapperProps> = (props) => {
  // ... rest of the code
  return <MemoizedFilterProvider roots={...} />;
};

Copy link
Author

@UNCANNY69 UNCANNY69 Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realised that the solution work in a completely unexpected way (Due to it referencing the component again its remounting and triggering initialize action , which is completely not right)

@UNCANNY69
Copy link
Author

UNCANNY69 commented Jan 30, 2025

Thanks for taking a stab at this, @UNCANNY69. While the memoization suggestion helps, the core issue appears to be deeper within the FilterContext flow. The RESET_TREE action in the FilterContext reducer fires correctly but fails to clear the checkboxes. There seems to be an issue with the FilterProvider state initialization that prevents re-initialization when the roots change.

To test this hypothesis out, try adding this force reset to FilterProvider that fires when roots changes:

   useEffect(() => {
     dispatch({ type: ReducerActionType.RESET_TREE });
   }, [roots]);

You'll see that this resets the filters. There's still some exploration to be done, but I think it's better to focus there for the moment

This is 100% right, this behaviour is not the right flow but this is a better solution then the one I mentioned above, will look into how I can prevent this.

image

The roots reference is changing i will se the way to prevent that

@UNCANNY69
Copy link
Author

@denniskigen I made the roots stable , thus this code below stopped working but the FilterProvider still retriggers

 useEffect(() => {
    dispatch({ type: ReducerActionType.RESET_TREE });
  }, [roots]);

can I try to change the location of the reset button of move the state to an upper level?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants