如何用React Hooks构建可复用的动画组件

原文:https://www.freecodecamp.org/news/animating-visibility-with-css-an-example-of-react-hooks/

译文:前端技术小哥


如何用React Hooks构建可复用的动画组件_第1张图片

动画取悦了用户。大家可能会认为,大量的文章表明,React Hooks取悦了开发人员。但对我来说,我已经开始对Hooks感到疲劳。

但机缘巧合下我改变了我的看法。我发现了一个很好地匹配React Hooks的示例,而不仅仅是“新方法”。正如大家根据本文的标题所猜测的,这个示例是一个动画。

我正在开发一个使用网格中的卡片的React应用程序。当一个项目被删除时,我想让它的退出动画化,就像这样。

如何用React Hooks构建可复用的动画组件_第2张图片

我的目标

不巧的是,这其实和我预计的有些许细微差别。而我的解决方案让我很好地利用了React Hooks。

我们要做什么?

  • 从一个基线示例应用程序开始

  • 逐渐增加消失的动画元素,把难点挑出来重点处理

  • 一旦我们实现了所需的动画,我们将重构一个可重用的动画组件

  • 我们将使用此组件使侧边栏和导航栏具有动画效果

  • 以及...(你需要读/跳到最后)

对于没有耐心的人,这里是这个项目的代码的GitHub repo。每个步骤都有标签。(有关每个标记的链接和描述,请参见自述)。

基线

我用create-react-app创建了一个简单的应用程序。它有一个简单的卡片网格。我们可以隐藏单独的卡片。

如何用React Hooks构建可复用的动画组件_第3张图片

没有动画-卡片消失得不连贯

这个的代码是很基础的,结果也是无趣的。当用户单击eye图标按钮时,我们会更改卡片的display属性。

function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);
  function hideMe() {
    setVisible(false);
  }
  let style = { borderColor: color, backgroundColor: color };
  if (!visible) style.display = "none";
  return (
    
{" "}
{word}
{" "} {" "}
); }

(是的,在上面我使用了Hooks,但这个用法并不有趣。)

添加动画

我没有建立自己的动画库,而是寻找类似animate.css的动画库。React-animated-css是一个很棒的动画库,它提供了一个围绕animate.css的包装。

npm install --save react-animated-css

把animate.css添加到index.html


在上面的Box组件中,我们将其渲染更改为
return (
  
    
{word}
);

和我们中预期的不相符

但是animate.css可以设置opacity和其他css属性的动画;我们不能在display属性上进行css转换。因此,一个不可见的对象仍然存在,它占用了文档流中的空间。如何用React Hooks构建可复用的动画组件_第4张图片

如果大家试着google一下,就会发现有一些解决方案建议使用计时器在动画结束时设置display: none

所以我们可以补充一点

function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);
  const [fading, setFading] = useState(false);


  function hideMe() {
    setFading(true);
    setTimeout(() => setVisible(false), 650);
  }


  let style = { borderColor: color, backgroundColor: color };


  return (
    
      
{word}
); }

(注意:默认动画持续时间为1000毫秒。我使用650毫秒作为超时,以便在设置display属性之前最小化停滞感。这是一个优先考虑的问题。)

这将给我们带来预期中的效果。

如何用React Hooks构建可复用的动画组件_第5张图片

奈思!

创建可复用组件

我们可以在这里停下来,但有两个问题(对我来说):

  1. 我不想复制/粘贴Animated块,样式和函数来重新创建此效果

  2. Box组件混合了不同类型的逻辑,即违反了关注点分离。具体来说,Box的基本功能是使用卡片呈现其内容。但动画细目混杂在了一起。

     

类组件

我们可以创建一个传统的React类组件来管理动画状态:切换可见性并设置displayCSS属性的超时。

class AnimatedVisibility extends Component {
  constructor(props) {
    super(props);
    this.state = { noDisplay: false, visible: this.props.visible };
  }


  componentWillReceiveProps(nextProps, nextContext) {
    if (!nextProps.visible) {
      this.setState({ visible: false });
      setTimeout(() => this.setState({ noDisplay: true }), 650);
    }
  }


  render() {
    return (
      
        {this.props.children}
      
    );
  }
}

然后用于

function Box({ word }) {
  const color = colors[Math.floor(Math.random() * 9)];
  const [visible, setVisible] = useState(true);


  function hideMe() {
    setVisible(false);
  }


  let style = { borderColor: color, backgroundColor: color };


  return (
    
      
{word}
); }

这的确创建了一个可复用组件,但它有点过于复杂。我们本可以做得更好。


React Hooks和useEffect

React Hooks是React 16.8中的新功能。它们为React组件中的生命周期和状态管理提供了一种更简单的方法。Hook useEffect 为我们使用componentWillReceiveProps提供了一个精妙的替代品。代码变得更简单,我们可以再次使用功能组件。

function AnimatedVisibility({ visible, children }) {
  const [noDisplay, setNoDisplay] = useState(!visible);
  useEffect(() => {
    if (!visible) setTimeout(() => setNoDisplay(true), 650);
    else setNoDisplay(false);
  }, [visible]);


  const style = noDisplay ? { display: "none" } : null;
  return (
    
      {children}
    
  );
}

使用useEffect hook有一些微妙之处。它主要用于副作用:更改状态、调用异步函数等。在我们的示例中,它根据先前的值visible设置内部noDisplay布尔值。通过向依赖关系数组的useEffect中添加visible,我们的useEffect hook只会在visible值发生更改时调用。

我认为useEffect是一个比类组件clutter.更好的解决方案,你们觉得呢?复用组件:Sidebars和Navbars

大家都喜欢Sidebars和Navbars。那我们分别加一个吧。

function ToggleButton({ label, isOpen, onClick }) {
  const icon = isOpen ? (
    
  ) : (
    
  );
  return (
    
  );
}


function Navbar({ open }) {
  return (
    
      
    
  );
}


function Sidebar({ open }) {
  return (
    
      
  • Item 1
  • Item 2
  • Item 3
); } function App() { const [navIsOpen, setNavOpen] = useState(false); const [sidebarIsOpen, setSidebarOpen] = useState(false); function toggleNav() { setNavOpen(!navIsOpen); } function toggleSidebar() { setSidebarOpen(!sidebarIsOpen); } return (
); }

如何用React Hooks构建可复用的动画组件_第6张图片

达成复用

但我们还没有结束...

我们可以在这里停下来。但正如我之前关于Separation of Concerns的评论一样,我宁愿避免在BoxSidebarNavbar的渲染方法中混合AnimatedVisibility组件。(这也是少量的重复。)

我们可以创建一个HOC。(事实上,我曾写了一篇关于动画和HOC的文章,如何在React中构建动画微交互。)但是由于状态管理,HOC通常涉及类组件。但是有了React Hooks,我们可以编写HOC(函数式编程方法)。

function AnimatedVisibility({
  visible,
  children,
  animationOutDuration,
  disappearOffset,
  ...rest
})
// ... same as before
}




function makeAnimated(
  Component,
  animationIn,
  animationOut,
  animationInDuration,
  animationOutDuration,
  disappearOffset
) {
  return function({ open, className, ...props }) {
    return (
      
        
      
    );
  };
}


export function makeAnimationSlideLeft(Component) {
  return makeAnimated(Component, "slideInLeft", "slideOutLeft", 400, 500, 200);
}


export function makeAnimationSlideUpDown(Component) {
  return makeAnimated(Component, "slideInDown", "slideOutUp", 400, 500, 200);
}


export default AnimatedVisibility
然后在App.js中使用这些基于函数的HOC
function Navbar() {
  return (
    
  );
}


function Sidebar() {
  return (
    
  • Item 1
  • Item 2
  • Item 3
); } const AnimatedSidebar = makeAnimationSlideLeft(Sidebar); const AnimatedNavbar = makeAnimationSlideUpDown(Navbar); function App() { const [navIsOpen, setNavOpen] = useState(false); const [sidebarIsOpen, setSidebarOpen] = useState(false); function toggleNav() { setNavOpen(!navIsOpen); } function toggleSidebar() { setSidebarOpen(!sidebarIsOpen); } return (
); }

比起冒着推广自己作品的风险,我更喜欢干净的结果代码。下面是最终结果的沙盒。(此处更改)

如何用React Hooks构建可复用的动画组件_第7张图片

现在还需要做什么呢?

对于简单的动画,我上面提到的方法效果很好。对于更复杂的情况,我会使用像react-motion这样的库。但除了制作动画,React Hooks提供了创建可读和简单代码的机会。但是,我们的思维需要调整。像useEffect这样的hooks并不是所有生命周期方法的直接替代品。我们需要需要大量的学习和实验。

我建议大家可以查看像useHooks.com这样的网站和像react-use这样的库,这是一个用于各种各样的用例的hooks集合。

希望本文能帮助到您!

看之后

  • 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)

  • 关注公众号「新前端社区」,享受文章首发体验!

  • 每周重点攻克一个前端技术难点。

你可能感兴趣的:(如何用React Hooks构建可复用的动画组件)