useEffect vs useEffect0


#1

Hey folks. I was trying to find documentation on useEffect vs useEffect0, unsuccessfully.

reproduction repo

I am trying understand what the difference is between:

 React.useEffect(() => {
    let timerId = Js.Global.setInterval(() => tick(), 1000);
    Some(() => Js.Global.clearInterval(timerId));
  });
 React.useEffect0(() => {
    let timerId = Js.Global.setInterval(() => tick(), 1000);
    Some(() => Js.Global.clearInterval(timerId));
  });

They both have the same type signature and both compile but the useEffect0 does nothing:

// useEffect0 : unit => option(unit => unit) => unit
// useEffect : unit => option(unit => unit) => unit

To use the example at https://reasonml.github.io/reason-react/blog/2019/04/10/react-hooks, it uses useEffect but if you change so that it uses useState instead of useReducer you have to change useEffect to useEffect0

Original version using useEffect0 on nit.sketch.sh:

type action =
  | Tick;

type state = {
  count: int,
};

[@react.component]
let make = () => {
  let (state, dispatch) = React.useReducer(
    (state, action) =>
      switch (action) {
      | Tick => {count: state.count + 1}
      },
    {count: 0}
  );

  React.useEffect0(() => {
    let timerId = Js.Global.setInterval(() => dispatch(Tick), 1000);
    Some(() => Js.Global.clearInterval(timerId))
  });
  
  <div>{ReasonReact.string(string_of_int(state.count))}</div>;
};

After removing useReducer and using useEffect on nit.sketch.sh:

[@react.component]
let make = () => {
let (state, dispatch) = React.useState(()=>
    {count: 0}
  );
let tick =()=> dispatch(_=>{count: state.count + 1});
  React.useEffect(() => {
    let timerId = Js.Global.setInterval(() => tick(), 1000);
    Some(() => Js.Global.clearInterval(timerId))
  });
  
  <div>{ReasonReact.string(string_of_int(state.count))}</div>;
};

So why does the call change when using the different structure.

Any links or explanation would be greatly appreciated.

Thank you.

(posted on stackoverflow)


#2

The obvious difference is that useEffect0 maps to useEffect(fn, []). I.e., it only runs once on mount as opposed to on every rerender. So, if you use useEffect0, the effect only runs once, and setInterval runs only once too and is never cleared. Yes, the lambda passed to setInterval is called multiple times, but it’s created on the first render and therefore refers to state from the closure of the first render.

By the way, I highly recommend Dan Abramoff’s A Complete Guide to useEffect. Your question was a great occasion to refresh some of the finer points, so thank you :slight_smile:

EDIT: wording, grammar.


#3

Btw, someone also answered on Stack Overflow, duplicating effort. Remember to link both ways when cross-posting a question on different forums :slight_smile:


#4

Thanks @hoichi @yawaramin.

@yawaramin i figured I would get different answers here which I did. I will of course be sure to cross post both ways next time. Thank you, sir.