lukeet
lukeet

Reputation: 491

how to add a transition to a an object when it renders in react?

So ive set up my code to render in a component into the HTML on the condition that a particular hook is set to true. Ive also set up a css transition but because when the element is set to true the css .open is triggered at exactly the same time it doesn't work how do i fix this I've looked into timed delays but they don't appear to work either.

//hooks set state
      const [mealOne_box, mealOne_boxSet] = useState(false);
      const [box_transition, setbox_transition] = useState(false);
      const [scroll, scrollSet] = useState(false);

//stops body from scrolling behind popup
     if (scroll) {
            document.body.style.overflow = 'hidden';
        } else {
            document.body.style.overflow = 'unset';
        }

//handles clicks for popup
      const mealOneClickHandler = (event) => {
            mealOne_boxSet(!mealOne_box);
            scrollSet(!scroll)
            setbox_transition(!box_transition)
        }


//popup element

  {mealOne_box && (
                <div className='meal_popup'>
                    <div className={box_transition ? 'meal_popupElement open' : 'meal_popupElement'}>
                        <CancelIcon onClick={mealOneClickHandler} />
                        <img alt='' src={PancakeImage} />
                        <div className='text_scroll'>
                            <h2>Method:</h2>
                            <p>blablabla</p>
                        </div>
                    </div>
                    <div onClick={mealOneClickHandler} className='meal_popupBackground' />
                </div>
            )}




//css

.meal_popupElement {
  position: fixed;
  margin: auto;
  margin-top: 6%;
  width: 95%;
  left: 50%;
  transform: translateX(-50%);
  background: white;
  border-radius: 15px;
  height: 80%;
  box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14),
    0px 1px 3px 0px rgba(0, 0, 0, 0.12);
  z-index: 2;
  overflow: hidden;

  p,
  h2 {
    padding-left: 1rem;
    padding-right: 1rem;
    font-family: "Open Sans", sans-serif;
  }
  p {
    margin-top: 0;
  }
  h2 {
    margin-bottom: 0.5rem;
    font-size: larger;
  }
  svg {
    position: absolute;
    display: flex;
    margin: 6px;
    width: auto;
    height: 35px;
    color: white;
    right: 0;
  }
  img {
    width: 100%;
    height: 30%;
    object-fit: cover;
    object-position: 0% 0%;
  }
  transition: opacity .25s ease-in-out;
  opacity: 0;
}

.meal_popupElement.open {
  opacity: 1;
}**

Upvotes: 1

Views: 1730

Answers (2)

Danziger
Danziger

Reputation: 21161

You need your component to render .meal_popup and then, once it's already in the DOM, add the .open class, so that the transition works.

One way to achieve that with minimal changes in your code would be to wrap setbox_transition(!box_transition) in a Window.requestAnimationFrame() or WindowOrWorkerGlobalScope.setTimeout() call to make sure mealOne_box and box_transition are not updated at the same time and trigger two separated request.

However, as setState is async, this might still not work, or it might be difficult to find the right delay for setTimeout, as React might decide to batch your updates and still do the DOM updates all at once.

This is probably going to work, but this delay is quite high:

const mealOneClickHandler = (event) => {
  mealOne_boxSet(!mealOne_box);
  scrollSet(!scroll);
  setTimeout(() => {
    setbox_transition(!box_transition);
  }, 1000);      
};

requestAnimationFrame might need to be added twice and still be more problematic than setTimeout:

const mealOneClickHandler = (event) => {
  mealOne_boxSet(!mealOne_box);
  scrollSet(!scroll);

  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      setbox_transition(!box_transition);
    });
  });      
};

So the best solution is to use ReactCSSTransitionGroup. You can take a look at this example which I think is easier to understand than the official docs.

Upvotes: 1

lukeet
lukeet

Reputation: 491

Ive fixed the fade in but not fade out:

 <div className= {mealOne_box ? 'meal_popup': 'meal_popup hidden'}>
                    <div className={box_transition ? 'meal_popupElement open' : 'meal_popupElement'}>
                        <CancelIcon onClick={mealOneClickHandler} />
                        <img alt='' src={PancakeImage} />
                        <div className='text_scroll'>
                            <h2>Ingredients:</h2>
                            <p>{Math.round((mealOneCals * 0.45) / 3.64)}g of flour, 1.5 teaspoons of baking powder, {Math.round((mealOneCals * 0.2) / 3.68)}g of cocoa powder, water, calorie free sweetener,  {Math.round((mealOneCals * 0.05) / 0.67)}g of mixed berries  and {Math.round(((mealOneCals * 0.3) / 1.55) / 44)} medium eggs.</p>
                            <p>High protein and low calorie dense option: use  {Math.round((mealOneCals * 0.55) / 3.64)}g of flour and {Math.round((mealOneCals * 0.2) / 0.45)}ml of egg white instead(this is less calorie dense so you get more food for the same amount of calories along with it being much higher in protein).</p>
                            <h2>Method:</h2>
                            <p>Combine the flour, egg, baking powder, cocoa together in a bowl to make a thick batter(add sweetener to taste). Then add as much water required to give the batter a pourable consistency. Pre heat a good non-stick pan on medium heat with no oil, once up to heat pour in your batter and flip once ready. Once all pancakes are made serve with fruit on-top.</p>
                        </div>
                    </div>
                    <div onClick={mealOneClickHandler} className='meal_popupBackground' />
                </div>

css

.meal_popup {
  position: fixed;
  display: flex;
  align-items: center;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 30;
}

.meal_popup.hidden{
  visibility: hidden;
}

Upvotes: 0

Related Questions