【问题标题】:Smooth animation when turning off a conditionally rendered component in React在 React 中关闭条件渲染组件时的平滑动画
【发布时间】:2022-01-17 20:24:51
【问题描述】:

我的 App.js 如下

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>
  );
};

我使用复选框设置ordering 变量的状态

然后我用它来有条件地渲染QuantityChange 组件,像这样

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>
  );
};

所有这些都可以正常工作,并且组件可以根据需要进行渲染。

我希望这个组件的进入和退出平滑过渡。进入动画效果很好,但我无法弄清楚如何让退出动画工作。

视频是现在正在发生的事情,可以在视频中找到https://youtu.be/5kl1wCBwR_U(复选框位于右下角)

我查看了几个在线论坛以找到答案,但我无法弄清楚。

我也尝试过使用react-transition-group,但没有成功

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>
  );
};

我查看了onAnimationEnd,但无法弄清楚。

【问题讨论】:

  • 您可以尝试拥有一个执行动画的 css 类,然后添加带有清理部分 (return () =&gt; doStuff()) 的 useEffect,该部分将在组件卸载之前触发,您可以在此清理函数中使用 setTimeout这将设置这个 css 类
  • @JSEvgeny 我想你指的是这个问题stackoverflow.com/questions/61428958/… 我试过了,但它不起作用。原因是 checkBox 上发生的状态变化和动画必须发生在单独的组件上。

标签: javascript reactjs css-animations conditional-rendering


【解决方案1】:

看起来你需要一个简单的手风琴东西。 你可以试试类似的东西(下面的sn-p)。

这里的主要时刻之一是将height 设置为auto 值。它允许内容发生变化,并且不会限制其尺寸。

AccordionItem 有条件地渲染它的孩子。如果它应该关闭并且动画结束,则不会渲染任何子节点。

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>

【讨论】:

  • 更新了我的 sn-p。现在AccordionItem 有条件地呈现它的孩子。如果它应该关闭并且动画结束,则不会渲染任何子节点。
  • Ivan,即使在您的代码中,也只能自动输入孩子,但退出却很突然。我希望出口也顺利。
  • 啊,等等……看起来它不像在 Chrome 中那样。让我解决它。
  • @moys,完成了。我不得不将“结束部分”包装到额外的 requestAnimationFrame 因为 Chrome 和 Safari 管理脚本调用的顺序有点不同。
猜你喜欢
  • 2021-12-20
  • 1970-01-01
  • 2021-06-07
  • 2018-09-10
  • 1970-01-01
  • 2021-09-28
  • 2020-08-09
  • 2015-05-02
  • 2020-05-03
相关资源
最近更新 更多