0

I create simple custom hook that save the screen height and width . The problem is that I want to re-render(update state) only if some condition in my state is happened and not in every resize event.. I try first with simple implementation :

const useScreenDimensions = () => {
  const [height, setHeight] = useState(window.innerWidth);
  const [width, setWidth] = useState(window.innerHeight);
  const [sizeGroup, setSizeGroup]useState(getSizeGroup(window.innerWidth));

 useEffect(() => {
  const updateDimensions = () => {
   if (getSizeGroup() !== sizeGroup) {
    setSizeGroup(getSizeGroup(width));
    setHeight(window.innerHeight);
    setWidth(window.innerWidth);
  }
};

  window.addEventListener('resize', updateDimensions);
  return () => window.removeEventListener('resize', updateDimensions);
  }, [sizeGroup, width]);

 return { height, width };

}

The problem with this approach is that the effect calls every time , I want that the effect will call just once without dependencies (sizeGroup, width) because I don't want to register the event every time there is a change in screen width/size group(window.addEventListener).

So, I try with this approach with UseCallBack , but also here my 'useEffect' function called many times every time there is any change in the state..

//useState same as before..
const updateDimensions = useCallback(() => {
  if (getSizeGroup(window.innerWidth) !== sizeGroup) {
  setSizeGroup(getSizeGroup(width));
  setHeight(window.innerHeight);
  setWidth(window.innerWidth);
}
}, [sizeGroup, width]);

useEffect(() => {
 window.addEventListener('resize', updateDimensions);
 return () => window.removeEventListener('resize', updateDimensions);
}, [updateDimensions]);

....
return { height, width };

The question is what the correct and effective way for my purposes? I want just "register" the event once, and update the my state only when my state variable is true and not every time the width or something else get updated..

I know that when you set empty array as second argument to 'UseEffect' it's run only once but in my case I want that the register of my event listener run once and on resize I will update the state only if some condition is true

Thanks a lot.

OriEng
  • 1,424
  • 18
  • 34
  • Possible duplicate of [How to call loading function with React useEffect only once](https://stackoverflow.com/questions/53120972/how-to-call-loading-function-with-react-useeffect-only-once) – Henry Mueller Apr 29 '19 at 04:29
  • @HenryMueller it's not duplicate . I know that when you set empty array as second argument to 'UseEffect' it's run only once but in my case I want that the register of my eventlistner run once and on resize I will update the state only if some condition is true... – OriEng Apr 29 '19 at 06:18

1 Answers1

1

use 2 different useEffect

first one for register event.So below code will run at the time of componentDidMount.

useEffect(() => {
  window.addEventListener('resize', updateDimensions);
}, []);

second useEffect to run based on state change.

useEffect(() => {
   updateDimensions();
   return () => window.removeEventListener('resize', updateDimensions);
}, [sizeGroup, width])


const updateDimensions = useCallback(() => {
 setSizeGroup(getSizeGroup(width));
 setHeight(window.innerHeight);
 setWidth(window.innerWidth);     
}

I'm not sure useCallback function need to use or not. And I've not tested this code.

Dhaval
  • 1,393
  • 5
  • 29
  • 55
  • Tnx, I try that but in this case eslint plugin 'force' me to set 'updateDimensions' function as dependency to the first 'useEffect' function , then what happend it's called every time there is a change in the state.. he want that dependency because in updateDimensions func I check some condition if to update state or not (sizegroup- you don't check that so maybe in your case eslint will not force you to set the func as dependency ) – OriEng Apr 29 '19 at 06:40
  • if you're passing [sizeGroup, width] to useEffect then you don't need to check condition. react hooks figure-out for you that when I need to run this function. so no need to if else condition. – Dhaval Apr 29 '19 at 06:42
  • So if sizeGroup and/or width will change only at that time updateDimensions function will call otherwise it'll not call. – Dhaval Apr 29 '19 at 06:43
  • if eslint force you to set updateDimensions as a dependency then in first useEffect add updateDimensions(); – Dhaval Apr 29 '19 at 06:45
  • But I don't want to update my height and width(in state) in every resize event,it's all the point. In your code every resize event when screen width change also my state change , that what I try to avoid from him..that my state width/height will change only if sizegroup changed... – OriEng Apr 29 '19 at 06:52
  • then use [sizeGroup] instead of [sizeGroup, width] – Dhaval Apr 29 '19 at 06:54
  • But I want to update my width also when sizegroup changed, and then when I add setWidth I have to set dependency of width...same loop ): – OriEng Apr 29 '19 at 07:16
  • 2
    setters have a functional form `setWidth(oldWidth => newWidth)` to be able to remove the dependency on `width`... – Aprillion Apr 29 '19 at 08:07
  • I still not understand how it solve my problem. For my check (group) you still have here dependency if the width has been changed or not.. and then again the useEffect force you to set func as dependency and call many times .. @Aprillion – OriEng Apr 29 '19 at 08:36
  • 1
    I think I just give up on 'group' under state and create function that calculate me the group by old width and new width like @Aprillion write , then I can set new width to state only if group are different. I don't see another way to think how can I update my width without dependency on group... – OriEng Apr 29 '19 at 08:57