5

I am trying to move the open state for material-ui dialog to redux to prevent it from closing when a rerender occurs, but i having trouble with the dialog when a rerender occurs. Although the state is saved in redux and the dialog does stay open whenever a rerender occurs the open state stays open but the dialog does show the open animation (fading in) which is kinda annoying.

Students.js (parent component of the modal)

const Students = ({
    app: { studentsPage: { savedAddDialogOpen }},
  setStudentsPageAddDialogOpen}) => {

    // Create the local states
    const [dialogOpen, setDialogOpen] = React.useState(savedAddDialogOpen);
    const dialogOpenRef = React.useRef(savedAddDialogOpen);

    // Change redux dialog open
    const setReduxDialogState = () => {
      setStudentsPageAddDialogOpen(dialogOpenRef.current, savedAddDialogOpen);
    };

    // Open add student dialog
    const dialogClickOpen = () => {
      setDialogOpen(true);
      dialogOpenRef.current = true;
      setTimeout(() => setReduxDialogState(), 300);
    };

    // Close add student dialog
    const dialogClose = () => {
      setDialogOpen(false);
      dialogOpenRef.current = false;
      setTimeout(() => setReduxDialogState(), 300);
    };

    return (
      <Container>
        {/* Add student modal */}
        <AddStudentModal dialogOpen={dialogOpen} dialogClose={dialogClose} />
      </Container>
    )

}

// Set the state for this component to the global state
const mapStateToProps = (state) => ({
  app: state.app,
});

AddStudentModal.js

const AddStudentModal = ({
    dialogOpen, dialogClose
    }) => {

    return (
      <Dialog
        open={dialogOpen}
      >
        {/* Lots of stuff*/}
        <DialogActions>
          <Button onClick={dialogClose}>
            Close dialog
          </Button>
        </DialogActions>
      </Dialog>
    )
};

I hope this should be sufficient. I tried checking if the open state is actually correct when a rerender occurs and it is correct every time but it looks like the dialog is closed at a rerender no matter what the open state is and only a few ms later actually notices that it should be opened.

Any help would be really appreciated

Edit 1: Found out it has nothing to do with the open state coming from redux, if i use open={true} it still flashes, so probably a problem with material-ui itself?

Edit 2: PrivateRoute.js

const PrivateRoute = ({
  auth: { isAuthenticated, loadingAuth },
  user: { loggedInUser },
  component: Component,
  roles,
  path,
  setLastPrivatePath,
  ...rest
}) => {
  useEffect(() => {
    if (path !== '/dashboard' && path !== '/profile') {
      setLastPrivatePath(path);
    }
    // Prevent any useless errors with net line:
    // eslint-disable-next-line
  }, [path]);

  // If we are loading the user show the preloader
  if (loadingAuth) {
    return <Preloader />;
  }

  // Return the component (depending on authentication)
  return (
    <Route
      {...rest}
      render={props =>
        !isAuthenticated ? (
          <Redirect to="/login" />
        ) : (loggedInUser && roles.some(r => loggedInUser.roles.includes(r))) ||
          roles.includes('any') ? (
          <Component {...props} />
        ) : (
          <NotAuthorized />
        )
      }
    />
  );
};

// Set the state for this component to the global state
const mapStateToProps = state => ({
  auth: state.auth,
  user: state.user
});

istvan kolkert
  • 122
  • 1
  • 10
  • 1
    No, this is not a problem with Material-UI. The behavior you are describing isn't caused by re-rendering, it is caused by re-mounting. Please reproduce your problem in a [code sandbox](https://codesandbox.io/s/new), and then others will be able to more easily help find the cause of the remount -- I don't see anything in the code currently included in your question that would cause this, so the cause is likely in code that you have not yet included in your question. – Ryan Cogswell Apr 10 '20 at 14:08
  • @RyanCogswell What would cause re-mounting, its not possible for me to show any more code than this without showing everything. If you could explain what could causes it maybe i can find something – istvan kolkert Apr 10 '20 at 14:34
  • The most common cause I see is a function component which is defined within another function component rather than at the top level. This means that anytime the containing component re-renders, the nested component will be a new type (due to the component's function being re-defined) which results in remounting. Examples: https://stackoverflow.com/questions/57433546/react-slider-with-custom-hook-not-working-properly/57434343#57434343, https://stackoverflow.com/questions/55192935/react-material-ui-menu-anchor-broken-by-react-window-list/55203218#55203218. – Ryan Cogswell Apr 10 '20 at 14:40
  • This problem (of a nested component definition) could be anywhere in the code around the components shown in your question, since a remounting of an ancestor will cause a remounting of all the descendant components. – Ryan Cogswell Apr 10 '20 at 14:41
  • Then i think i found it, i have a privateRoute component which checks if i am still authenticated, i will add it to the question – istvan kolkert Apr 10 '20 at 14:42
  • I don't see anything in `PrivateRoute` that is inherently problematic, but it would be helpful to see how it is used (particularly to see what exactly you pass to the `component` prop). – Ryan Cogswell Apr 10 '20 at 14:50
  • FYI - you can find more examples of answers I have provided for re-mount issues in these search results: https://stackoverflow.com/search?q=user%3A7495930+re-mount – Ryan Cogswell Apr 10 '20 at 14:53
  • As I mentioned in my initial comment, the troubleshooting route I would recommend is to create a code sandbox with the simplest possible attempt to reproduce the problem. If it doesn't reproduce it, gradually make the sandbox more similar to your actual code until you successfully reproduce the problem. – Ryan Cogswell Apr 10 '20 at 14:57
  • @RyanCogswell I will try to make a sandbox, and the component prop is the actual component which will be displayed so for example students.js – istvan kolkert Apr 10 '20 at 15:03
  • 1
    I found the problem!, thanks for sending me in the right direction! I will add the solution – istvan kolkert Apr 10 '20 at 15:17

1 Answers1

2

I found the problem thanks to @RyanCogswell!

For anyone having the same problem here is the cause for me and the fix:

I was passing components into the Route component like this:

<PrivateRoute
   exact
   path={'/dashboard/students'}
   component={(props) => (
     <Students {...props} selectedIndex={selectedIndexSecondary} />
   )}
   roles={['admin']}
/>

By doing it this way i could pass props through my privateRoute function but this would also happen if you send the component this way in a normal Route component

Solution for me is just moving selectedIndexSecondary to redux and sending the component the normal way it prevented the re-mounting.

So just doing it like this will prevent this from happening.

<PrivateRoute
   exact
   path={'/dashboard/students'}
   component={Students}
   roles={['admin']}
/>

Also this will solve the localstates in your components from resseting to the default value. So for me it fixed two problems!

istvan kolkert
  • 122
  • 1
  • 10