Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why useEffect doesn't fire on every render?

My component has two Rect.useEffect hook

const Example = ({ user }) => {
  React.useEffect(() => {
    autorun(() => {
      console.log("inside autorun", user.count);
    });
  });

  // Only runs once
  React.useEffect(() => {
    console.log("Why not me?");
  });

  return <Observer>{() => <h1>{user.count}</h1>}</Observer>;
};

I update this component using mobx. It is re-rendered correctly. But "Why not me?" is printed only once.

As per official docs

By default, effects run after every completed render

This means console.log("Why not me?"); too should run every time prop user is updated. But it doesn't. The console output is this

What's the reason behind this apparent inconsistency?

My complete code can be viewed here

Edit mobx-useEffect query

like image 598
Andrew-Dufresne Avatar asked Apr 01 '19 09:04

Andrew-Dufresne


People also ask

Does useEffect run after every render?

The useEffect Hook Usages. The callback function we pass to the useEffect hook runs the side effects. React runs it on every render of a component by default.

How do I make useEffect run everytime?

By default, useEffect runs after every render, but it's also perfect for running some code in response to a state change. You can limit when the effect runs by passing the second argument to useEffect. Think of the second argument as an array of “dependencies” – variables that, if changed, the effect should rerun.

Does useEffect always run on first render?

By default, useEffect will run on initial render as well as every future render (update) of your component.

What is the one problem with using useEffect?

The infinite re-renders problem The reason our component is re-rendering is because our useEffect dependency is constantly changing. But why? We are always passing the same object to our hook! While it is true that we are passing an object with the same key and value, it is not the same object exactly.


1 Answers

In Mobx, just like Observer component which provides a render function callback, autorun function also executes independently of the react lifecycle.

This behaviour happens because you have user count as a observable variable.

According to the mobx-react docs

Observer is a React component, which applies observer to an anonymous region in your component. It takes as children a single, argumentless function which should return exactly one React component. The rendering in the function will be tracked and automatically re-rendered when needed.

and mobx docs

When autorun is used, the provided function will always be triggered once immediately and then again each time one of its dependencies changes.

You can confirm this behvaior by logging directly inside the functional component and you will observer that the component is only rendered once

EDIT:

To answer your question

If I change useEffect to this

React.useEffect(autorun(() => {console.log("inside autorun", user.count)}));

basically remove anonymous function from useEffect and just pass autorun directly, then it is run only once. Why is it so? What's the difference?

The difference is that autorun returns a disposer function which when run will dispose of the autorun and would not longer execute it.

From the docs:

The return value from autorun is a disposer function, which can be used to dispose of the autorun when you no longer need it.

Now what happens is that since useEffect executes the callback provided to it when it runs, the callback executed is the disposer function returned by autorun which essentially cancels your autorun.

like image 84
Shubham Khatri Avatar answered Oct 01 '22 03:10

Shubham Khatri