1
\$\begingroup\$

I am using Redux Toolkit and have a general-purpose ui-slice that I use for UI states that I need globally.

In particular I have few components that need to know the current width of the window to conditionally render some content. So I made this reducer:

acknowledgeScreenWidth(state, action) {
    state.screenWidth = action.payload.screenWidth;
},

I dispatch against this reducer only in one place, in a layout component, like so:

useEffect(() => {
    const updateWidth = () => dispatch(uiActions.acknowledgeScreenWidth({ screenWidth: window.innerWidth }));
    updateWidth();
    window.addEventListener('resize', updateWidth);
    return () => window.removeEventListener('resize', updateWidth);
  }, []);

And then in the components where I need the current width I have:

const screenWidth = useSelector(store => store.ui.screenWidth);
const isSmall = screenWidth < 768;

Which works because every time screenWidth changes, the component rerenders and isSmall is redeclared.

However the components also have other states and there is no need to redeclare isSmall every time they rerender. (furthermore, the window width is likely to be always the same from the beginning to the end)

So I wonder if creating a local state:

const screenWidth = useSelector(store => store.ui.screenWidth);
const [isSmall, setIsSmall] = useState(true);

useEffect(() => {
  setIsSmall(screenWidth < 768)
}, [screenWidth]);

Or memoizing isSmall:

const screenWidth = useSelector(store => store.ui.screenWidth);
const isSmall = useMemo(() => screenWidth < 768, [screenWidth]);

Could be a better approach, or is actually an anti-pattern.

\$\endgroup\$
1
\$\begingroup\$

Performance

I don't think that you need to change the approach you are taking with isSmall, it is very cheap to create that variable each render. The other two approaches you posted would actually be worse in performance. You can read further on this wonderful blog by Kent C. Dodds.

Ultimately, if it really bothers you, you should measure it. Profile the renders and see which way it is faster.

useEffect

The code you posted is potentially buggy. The dependency array is missing some dependencies:

useEffect(() => {
    const updateWidth = () => dispatch(uiActions.acknowledgeScreenWidth({ screenWidth: window.innerWidth }));
    updateWidth();
    window.addEventListener('resize', updateWidth);
    return () => window.removeEventListener('resize', updateWidth);
  }, [dispatch, uiActions]);

I think that you should probably use the useLayoutEffect hook because I think that your app might flicker twice before setting the new width on resizing, but I am just guessing since I don't have the reproducible example. Usually, when you are dealing with layout transformations, you would use this hook.

The approach

Your approach looks good to me.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ After writing the question I reasoned too that the other two approaches couldn't perform better, but I thought that maybe there was some best practice to follow. Anyway I read about useLayoutEffect and it fits perfectly in this case. Thanks for this amazing discovery \$\endgroup\$ Jul 9 at 1:51
  • 1
    \$\begingroup\$ I'm glad it helped @SheikYerbouti \$\endgroup\$
    – alexfertel
    Jul 9 at 1:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.