React Hook useEffect has missing dependencies: 'classes' (Material-ui styles and react-bootstrap-sweetalert)

Viewed 538

I'm having this warning: "React Hook useEffect has missing dependencies: 'classes.button' and 'classes.warning'."

The issue here is that, according to the recommended usage of react-bootstrap-sweetalert, I should set SweetAlert component in state (I don't know if this is a good practice, but is how they suggest to use it).

import { makeStyles } from "@material-ui/core/styles";
import styles from "assets/jss/views/Alerts";
const useStyles = makeStyles(styles);

export default function Alerts() {
  const [alert, setAlert] = useState(null);

  useEffect(() => {
    fetch('api').then(res => res.json()).then(data => setData(data))
      .catch(err => {

        setAlert(
          <SweetAlert
            style={{ display: "block", marginTop: "-100px" }}
            title="Error"
            onConfirm={() => {setAlert(null); setLoading(false)}}
            onCancel={() => {setAlert(null); setLoading(false)}}
            confirmBtnCssClass={classes.button + " " + classes.warning} // HERE!
          >
            error
          </SweetAlert>
        )

      })
  }, [])

  const classes = useStyles();
  return (
    <div>
     {alert}
     ...
    </div>
  )
}

I have multiple alert components and that is why they recommend that usage. I'd like to know the proper way to solve this.

2 Answers

@EdLucas's first answer is the right solution according to the given question.

When you use useEffect every state, props and some functions inside it must be a dependency, even if it never changes or if it is a function (that dependens on other functions, props or state) or if it changes too often. The issue here is that we would like to add into dependencies array what we want to execute callback and not what it supossed to be according to this issue.

This is about an easier readable code vs preventing bugs. Dan Abramov (gaearon)'s answer was: "So far in our experience the issues caused by missing dependencies have been significantly worse than losing the quick “[] means mount” visual shortcut." and that's why is not all about what should execute the callback but about what might cause bugs.

This is what I did:

const errorAlert = useCallback(
(message = 'Try again later') => {
  setAlert(
    <SweetAlert
      style={{ display: "block", marginTop: "-100px" }}
      title="Error"
      onConfirm={() => {setAlert(null); setLoading(false)}}
      onCancel={() => {setAlert(null); setLoading(false)}}
      confirmBtnCssClass={classes.button + " " + classes.warning}
    >
      {message}
    </SweetAlert>
  );
},
[classes.button,classes.warning]);

then I can use it like this:

useEffect(() => {
 fetch('api')
  .then(res => res.json())
  .then(data => setData(data))
  .catch(err => errorAlert())
}, [errorAlert]);

errorAlert function is wrapped into useCallback following the recommendations on react docs: https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

  1. errorAlert can not be moved into useEffect because it's used by others functions
  2. errorAlert can not be moved outside the component because it uses 'classes'
  3. As last resort I'm wrapping errorAlert function into useCallback to ensure it doesn't change on every render And unless 'classes.button' and 'classes.warning' change (it should never happen) then useEffects that have errorAlert dependencies (all of them) should be executed again.

If you have a classes object outside of your useEffect() which holds your button styles, then you should pass in your classes object as a dependency:

useEffect(() => {
  ...
}, [classes]);

Otherwise, you can just hard code the button classes:

confirmBtnCssClass={'btn btn-warning'}
Related