0

I have these components without any recoil's hooks.

const C = () => {
  console.log('---->C')
  return <Text>C</Text>
}
const B = () => {
  console.log('--->B')
  return <>
    <Text>B</Text>
    <C/>
  </>
}
const A = () => {
  console.log('-->A')
  return <>
    <Text>A</Text>
    <B/>
  </>
}

const App = () => {
  console.log('->App')
  return (
    <RecoilRoot>
      <A />
    </RecoilRoot>
  );
};

when i run the app in the console show up the spected logs:

 LOG  ->App
 LOG  -->A
 LOG  --->B
 LOG  ---->C

now im gonna use recoil hooks to mutate and access the atom state

import { atom, useSetRecoilState, useRecoilState, useRecoilValue, RecoilRoot } from "recoil";
const atomTest = atom({
  key: "abcatomTest",
  default: "A"
})
const C = () => {
  console.log('---->C')
  const [value, set] = useRecoilState(atomTest)
  return <>
    <Text>C</Text>
    
  </>
}
const B = () => {
  console.log('--->B')
  const set = useSetRecoilState(atomTest)

  return <>
    <Text>B</Text>
    <C/>
  </>
}
const A = () => {
  console.log('-->A')
  const value = useRecoilValue(atomTest)
  
  return <>
    <Text>A</Text>
    <B/>
  </>
}

const App = () => {
  console.log('->App')
  return (
    <RecoilRoot>
      <A />
    </RecoilRoot>
  );
};

i dont even using the values and functions returned from useRecoilValue, useSetRecoilState, useRecoilState, if i use it, it works properly, BUT in the very first render the logs are:

 LOG  ->App
 LOG  -->A
 LOG  -->A
 LOG  ->App
 LOG  --->B
 LOG  ---->C
 LOG  ---->C
 LOG  --->B
 LOG  -->A
 LOG  ->App
 LOG  -->A
 LOG  --->B
 LOG  ---->C

why is recoil forcing the re-render of multiple components including root, im not mutating the state at all, and in the App component there is not dependency to any state neither!

adrian oviedo
  • 684
  • 1
  • 8
  • 27

1 Answers1

1

First of all: React executing a function does not mean that the component actually re-renders. React has a commit and a rendering phase. During the commit phase React goes through changes and calls the child components, checking if there is anything new to render. During the rendering phase React checks if there are components that actually have to re-render. If the outputs, hook states and props are identical there will be no re-render, even though React previously called your function component. This is why you see all the logs. You are not checking for re-renders with that, but for function executions.

Your App component actually has dependencies to state, since it renders the RecoilRoot component. When that component changes, React will enter the commit phase again and go through all children, to see if there are changes.

Since every component uses a hook that references the atomTest atom, Recoil has to subscribe to that atom for that components. So Recoil as well as React have to look for changes through the tree.

If you check with the Profiler of the React Developer Tools you'll see that there are no actual re-renders, since your components didn't change any output.

Johannes Klauß
  • 10,676
  • 16
  • 68
  • 122