【问题标题】:How can I use React Material UI's transition components to animate adding an item to a list?如何使用 React Material UI 的过渡组件动画将项目添加到列表?
【发布时间】:2020-10-14 03:31:09
【问题描述】:

我有这门课。

class Demo extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      items: []
    };

    this.add = this.add.bind(this);
    this.clear = this.clear.bind(this);
  }

  add() {
    this.setState(prev => {
      const n = prev.items.length;
      return {
        items: [<li key={n}>Hello, World {n}!</li>, ...prev.items]
      };
    });
  }

  clear() {
    this.setState({ items: [] });
  }

  render() {
    return (
      <div>
        <div>
          <button onClick={this.add}>Add</button>
          <button onClick={this.clear}>Clear</button>
        </div>

        {/* This is wrong, not sure what to do though... */}
        <Collapse in={this.state.items.length > 0}>
          <ul>{this.state.items}</ul>
        </Collapse>
      </div>
    );
  }
}

沙盒链接:https://codesandbox.io/s/material-demo-ggv04?file=/Demo.js

我试图做到这一点,以便每次单击“添加”按钮时,都会在列表顶部生成一个新项目,并且将现有项目下推。不知道如何继续。

额外资源

【问题讨论】:

    标签: javascript reactjs material-ui


    【解决方案1】:

    我更新了你的 Sandbox 代码来实现你想要的,但我不认为 MaterialUI 是最好的库(我可能会错过更好的方法)。

    挑战在于,当您添加一个新项目时,它还不存在于 DOM 中。而且大多数动画库/组件都要求元素位于 DOM 中,它们只是“隐藏”和“显示”它并带有过渡时间。

    我也遇到过类似的情况,经过一些研究,我发现可以处理 DOM 中尚未包含的元素动画的更好的库是 Framer Motion。 (你可以查看他们的文档mount animations

    无论如何,这里是新的Code Sandbox 的链接,所以你可以看看。我所做的更改:

    移除随机密钥

    在使用&lt;Collapse /&gt; 组件创建列表的map 函数中,有一个函数可以获取随机整数并将其作为key 分配给您的组件。 React 需要有一致的键才能正确地进行伪装,因此删除该随机数可以解决您的“切换”按钮没有正确动画的问题。 (如果你的item列表没有唯一ID,就使用map函数的索引,这不是一个好的解决方案,但还是比随机数好)。

    <Collapse key={i} timeout={this.state.collapseTimeout} in={this.state.open}>
        {it}
    </Collapse>
    

    添加了控制切换的新功能

    这里的方法是:将项目添加到列表中,在元素进入 DOM 后,关闭&lt;Collapse /&gt;,稍等片刻,然后再次打开它(这样您就可以直观地看到动画)。为此,我们需要一个新的“切换”函数,它可以显式设置折叠的值。

    toggleValue(value) {
      this.setState(() => {
        return {
          open: value
        };
      });
    }
    

    为折叠添加了可变超时时间

    最后一个问题是,在添加新项目时关闭 &lt;Collapse /&gt; 会触发动画以将其关闭。这里的解决方案是动态改变崩溃的超时时间,所以你看不到。

    setCollapseTimeout(value) {
      this.setState(() => {
        return {
          collapseTimeout: value
        };
      });
    }
    

    将元素添加到列表时,等待触发动画 同样,要解决尚未在 DOM 中的元素的问题,我们需要使用 setTimeout 或等待切换 &lt;Collapse /&gt; 的东西。这是在您的 add() 函数中添加的。

    add() {
      this.toggleValue(false);
      this.setCollapseTimeout(0);
      this.setState(prev => {
        const n = prev.items.length;
        return {
          items: [<li key={n}>Hello, World {n}!</li>, ...prev.items]
        };
      });
      setTimeout(() => {
        this.setCollapseTimeout(300);
        this.toggleValue(true);
      }, 100);
    }
    

    同样,这是一个让 MaterialUI 中的 &lt;Collapse /&gt; 与尚未在 DOM 中的元素一起工作的 hacky 解决方案。但是,如前所述,还有其他更好的库。

    祝你好运:)

    【讨论】:

    • 啊,哎呀。我的意思是创建一个新的沙箱进行实验,对不起。切换的东西是无关紧要的。感谢您的帮助!很好的信息。
    【解决方案2】:

    早些时候在这里结束,然后回来创建一个沙盒,希望为这种场景提供一个简单的方法。 material-ui 文档在这方面有点(很多)轻松,我正在与非常相似的情况作斗争,但我尝试了来自 react-transition-groupTransitionGroup 的一些东西,交叉手指,它似乎有效。

    Forked CodeSandbox with TransitionGroup

    关键是你

    1. 将所有要转换的组件包装在&lt;TransitionGroup&gt; 组件中
    2. TransitionGroup 中,为要呈现的数据输入“条件”(逻辑或循环输出)
    3. 用您选择的转换组件包装您要转换的各个组件 - 在本例中为 &lt;Collapse&gt;

    例如在其最简单的设置中,“items”是一组来自 props、state 或 redux store 的唯一数字

    <TransitionGroup>
      {items.map(item => (
          <Collapse key={item}>
              I am item {item}
          </Collapse>
      ))}
    </TransitionGroup>
    

    通过这个设置,我发现我不需要在TransitionGroupCollapse 上放置任何道具,TransitionGroup 处理了循环渲染中的所有安装和卸载。 Material UI 不会产生最轻量的 HTML 输出,但我想它都是动态渲染的,所以也许这会更好(除非你有数千个元素,然后事情开始拖累)。

    您甚至可以更进一步,将整个内容包装在另一个 TransitionGroup 中,以涵盖您想要删除整个内容而不转换所有单个项目的情况 - 在这种情况下,我将其切换为 &lt;Slide&gt; .我绝对确定这行不通,但它似乎不在乎。您也可以尝试语义化并使用“组件”属性,而不是包装在另一个元素中,例如

    <TransitionGroup>
      {items.length > 0 && (
        <Slide>
          <TransitionGroup component="ul">
            {items.map((item) => (
              <Collapse component="li" key={item}>I am item {item}</Collapse>
            ))}
          </TransitionGroup>
        </Slide>
      )}
    </TransitionGroup>
    

    我通过以下方式更改了沙盒

    • 包括TransitionGroup 来自react-transition-group
    • 更改了“添加”逻辑,使组件不属于“项目”数组 - 该数组仅包含渲染组件所需的数据
    • 我添加了一个简单的“计数”并将其推送到数组中,以便为项目提供唯一索引(最初使用Math.random,但我想要一个“更漂亮”的输出)。通常,您的商品可能来自已设置唯一 ID 的数据库。
    • 根据数组中的数据循环渲染组件(这可以在单独的函数中完成,但要点是组件没有存储在数组中)
    • 添加了“删除”功能以显示单个项目的删除
    • 将整个组包装在第二个 &lt;TransitionGroup&gt; 中,以表明可以在组级别进行卸载
    • 添加一些简单的样式以更好地了解效果。您可以在此处使用 Material UI 组件,但只是想保持简单。

    希望这对将来的人有所帮助。

    【讨论】:

      猜你喜欢
      • 2021-05-13
      • 1970-01-01
      • 2020-08-29
      • 2019-07-02
      • 2019-12-05
      • 2020-02-11
      • 1970-01-01
      • 2022-11-11
      • 2018-10-26
      相关资源
      最近更新 更多