My App.js is as below
export const App = () => { const [toggled, setToggled] = useState(false); const [ordering, setOrdering] = useState(false); const handleColorModeClick = () => { setToggled((s) => !s); }; const handleOrdering = () => { setOrdering((s) => !s); }; return ( <Ordering.Provider value={{ ordering: ordering }}> <div className={`app ${toggled ? "theme-dark" : "theme-light"}`}> <Switch> <Route path="/" exact> <HeaderComponent toggled={toggled} onClick={handleColorModeClick} /> <div>components2</div> <EateryInfo toggled={toggled} /> {/* <CategoryItems toggled={toggled} /> */} <MenuButton toggled={toggled} /> </Route> <Route path="/menu"> <HeaderComponent toggled={toggled} onClick={handleColorModeClick} /> <CategoryItems toggled={toggled} /> <CheckBox text="Start Ordering" standAlone={true} handleOrdering={handleOrdering} /> <MenuButton toggled={toggled} /> </Route> </Switch> </div> </Ordering.Provider> ); };
I set the state of ordering
variable using a checkbox
Then I use this to conditionally render the QuantityChange
component like so
export const MenuEntry = ({ mealData, toggled }: MenuEntryProps) => { const orderingEnabled = useContext(Ordering); return ( <div className="menu-entry"> <MenuItem oneMenuItem={mealData} toggled={toggled} /> {orderingEnabled.ordering ? <QuantityChange toggled={toggled} /> : ""} </div> ); };
All this works fine & the component is render as desired.
I want to have a smooth transition of entry & exit of this component. The animation on entry works just fine but I am not able to figure out how to get the exit animation working.
The video is what is happening now can be found in the video here https://youtu.be/5kl1wCBwR_U (the checkbox is at the right bottom hand corner)
I looked at several online forums to find an answer to this but I am unable to figure it out.
I tried usiing react-transition-group
as well but no luck
export const QuantityChange = ({ toggled }: QuantityChangeProps) => { const orderingEnabled = useContext(Ordering); const duration = 500; return ( <Transition in={orderingEnabled.ordering} timeout={duration} appear> {(status) => ( <div className={`quantity-change flex ${ toggled ? "theme-dark" : "theme-light" } fade-${status}`} > <span className="add-quantity">+</span> <span className="quantity">0</span> <span className="subtract-quantity">-</span> </div> )} </Transition> ); };
I looked at onAnimationEnd
but was unable to figure it out.
Advertisement
Answer
Looks like you need a simple Accordion thingy. You could try something like that (snippet below).
One of the main moments here is setting the height
to the auto
value. It allows the content to change, and it won’t strict its dimensions.
AccordionItem
conditionally renders its children. If it should be closed and the animation is over, then no children will be rendered.
const AccordionItem = (props) => { const { className, headline, open, children } = props const [height, setHeight] = React.useState(0) const [isOver, setOver] = React.useState(false) const bodyRef = React.useRef(null) const getDivHeight = React.useCallback(() => { const { height } = bodyRef.current ? bodyRef.current.getBoundingClientRect() : {} return height || 0 }, []) // set `auto` to allow an inner content to change const handleTransitionEnd = React.useCallback( (e) => { if (e.propertyName === 'height') { setHeight(open ? 'auto' : 0) if (!open) { setOver(true) } } }, [open] ) React.useEffect(() => { setHeight(getDivHeight()) setOver(false) if (!open) { requestAnimationFrame(() => { requestAnimationFrame(() => setHeight(0)) }) } }, [getDivHeight, open]) const shouldHide = !open && isOver return ( <div style={{overflow: 'hidden'}}> <div style={{ height, transition: "all 2s" }} onTransitionEnd={handleTransitionEnd} > <div ref={bodyRef}> {shouldHide ? null : children} </div> </div> </div> ) } const App = () => { const [open, setOpen] = React.useState(false) return ( <div> <button onClick={() => setOpen(isOpen => !isOpen)}>toggle</button> <table style={{width: '100%'}}> <tr> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> </tr> <tr> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> </tr> <tr> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> <td> Hot Pongal <AccordionItem open={open}> <button>-</button> <input /> <button>+</button> </AccordionItem> </td> </tr> </table> </div> ) } ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script> <div id="root"></div> <div id="root"></div>