2

I needed bootstrap cards of equal heights inside a react-slick carousel. I found a solution from the following post: React Slick Slide Height Issue

Below is my code, which records the height of the carousel div containing the bootstrap cards. The captured height is then passed to the bootstrap cards as a prop.

    const [upcomingEventsHeight, setUpcomingEventsHeight] = React.useState("auto");
    React.useEffect(() => {
        setTimeout(
            () => {
                const height = document.getElementsByClassName("slick-track")[0].clientHeight;
                setUpcomingEventsHeight(height + "px");
            }, 3000);
    }, []);

It works fine, except when the browser is resized, because the height is only set when the page is first rendered.

I think that I need to re-render the page when the browser is resized, and I know that I can use window.addEventListener('resize', ...);

However, I get the error with the following code because React.useEffect cannot be called inside a callback.

    window.addEventListener('resize', () => {
        React.useEffect(() => {
            setTimeout(
                () => {
                    const height = document.getElementsByClassName("slick-track")[0].clientHeight;
                    setUpcomingEventsHeight(height + "px");
                }, 3000);
        }, []);
    });

I'm not sure how to get around this because I want to use useEffect to re-render the page.

  • The first rule of hooks - it must be at the top level of component. so your requirement is wrong – programoholic Sep 17 '21 at 19:07
  • Why do you need an `useEffect` on resize? Have you tried to put your code directly on the event listener? If it is because of the `setTimeout`, you can try to do something using `useRef` to store its reference – Rafael Tavares Sep 17 '21 at 19:10
  • I'm not a React guy so forgive any ignorance - but why not just set `height:100%` in the CSS ruleset for the element? – Quasipickle Sep 17 '21 at 19:29

3 Answers3

2

This is tricky but trivial for lots of projects. Your useEffect will be trigged one time and the function inside will be trigged everytime the user resize the browser.

Sometimes when you resize the browser you cannot access state or set the state (using useState) so i like to put the value of the width of the window outside. This way i can reuse the values anytime with almost no conflicts.

// react using hooks
import { useState, useEffect } from 'react';

// use outside the component
let isMobile = true; // variable to check width

const MyComponent = () => {

    // optional: save the width of the window using state
    const [width, setWidth] = useState(window.innerWidth); // check width size of the window
    const handleWindowSizeChange = () => {
        setWidth(window.innerWidth);
        isMobile = window.innerWidth < 700 ? true : false;
    };

    // call your useEffect
    useEffect(() => {
        window.addEventListener('resize', handleWindowSizeChange);
        return () => {
            window.removeEventListener('resize', handleWindowSizeChange);
        };
    }, []);

    // view of the component
    return (<h1>{isMobile}</h1>)

}
Coffezilla
  • 376
  • 2
  • 7
1

You should actually use useLayoutEffect to handle this kind of computation, because it is triggered while the component renders (after the computation is done, but before the painting is done), as opposed to useEffect, that runs after the component has rendered.

DoneDeal0
  • 5,273
  • 13
  • 55
  • 114
0

You want to use useEffect to add the event listener (and remove it).

const handleResize = () => {
   ...
}

React.useEffect(() => {
  window.addEventListener('resize', handleResize)

  return () => window.removeEventListener('resize', handleResize)
}, [])

This will add the event listener when the component is rendered, and remove it when it's not (the return value of useEffect is run on cleanup). useEffect will run every time a variable in the array in the second parameter changes (in this case it's left as empty [], so it will only run once).

Also, instead of using setTimeout, you should consider using a "debounced" function, this post explains how they work. You can find debounce functions to reuse on npm and in lodash etc.

Matthew Rapati
  • 5,648
  • 4
  • 28
  • 48
  • The handleResize declaration should be in the React.useEffect. As it is now, handleResize will be redefined on every render. Should always prefer to narrow the scope of our variables. – Jeff Walters May 25 '22 at 17:50