如何使用react-transition-group触发动画

时间:2019-06-21 09:31:33

标签: reactjs

问题是我的表单具有三种状态:错误,信息和成功。取决于服务器的响应是否使用上述状态触发烤面包机,当服务器的响应可用时,我需要添加淡入淡出动画。

toasterService.js

 import React, {useState} from 'react';
 import {Transition} from 'react-transition-group';
 import './toasterService.css'

 export default function ToasterService(content, timeout, style) {
const inProp = useState(true); // always call hook on top level
const duration = timeout;
const transitionStyles = {
    entering: {opacity: 1},
    entered: {opacity: 1},
    exiting: {opacity: 0},
    exited: {opacity: 0},
};

let defaultStyle = {};

switch (style) {
    case 'info' :
        defaultStyle = {
            transition: `opacity ${duration}ms ease-in-out`,
            opacity: 0,
            backgroundColor: '#00c5dc',
            color: '#ffffff'
        };
        break;

    case 'success' :
        defaultStyle = {
            transition: `opacity ${duration}ms ease-in-out`,
            opacity: 0,
            backgroundColor: '#8ebe4b',
            color: '#ffffff'
        };
        break;
    case 'danger' :
        defaultStyle = {
            transition: `opacity ${duration}ms ease-in-out`,
            opacity: 0,
            backgroundColor: '#FF0000',
            color: '#ffffff'
        };
        break;
    default :
}


return (<div className="main-alert">

        <Transition in={inProp} timeout={duration}>
            {state => (
                <div style={{
                    ...defaultStyle,
                    ...transitionStyles[state]
                }}>
                    {content}
                </div>
            )}
        </Transition>
    </div>

);
}

Login.js

import ToastService from '../../services/core/toasterService';
// on click of login btn
  socialSignIn = () => {
    let obj = {};
    obj = this.state;

    fetch(url,
        {
            method: 'post',
            body: JSON.stringify(obj)

        }).then(function (res) {
        console.log(res.json());
          ToastService('success', 5000,'info');
        return res.json();

    })
};

Toast Service接收3个参数,但未显示烤面包机。我想念的是什么?

1 个答案:

答案 0 :(得分:0)

我最近亲自构建了一个Toastr组件,使用了styled-componentsanimejsreact-transition-group这些基本功能,可以帮助您正确使用它。

注意::我认为使用animejs比为过渡的每个阶段设置样式都容易。基本上,您可以获取进入或退出元素的参考,并使用animejs对其进行动画处理。

react-transition-group将为您提供这些道具中的元素的参考:

<Transition
  key={item.id}
  onEntering={animateEnter}  // animateEnter will have a reference to the element
  onExiting={animateExit} // animateExist will have a reference to the element
  timeout={{
    enter: 500,
    exit: 500
  }}
  unmountOnExit={true} // I was testing, but I don't think this prop is necessary in my component
>

See working example on CodeSandbox

enter image description here

这是代码:

index.js

import React from "react";
import ReactDOM from "react-dom";
import Toastr from "./Toastr";
import SomeComponent from "./SomeComponent";

import "./styles.css";

function App() {
  return (
    <Toastr>
      <SomeComponent />
    </Toastr>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Toastr.js

import React, { useRef, useEffect, useState } from "react";
import styled from "styled-components";
import { TransitionGroup, Transition } from "react-transition-group";
import anime from "animejs";
import ToastrContext from "./ToastrContext";

// CREATE A USE TOASTER HOOK ?
// MAYBE CREATE AN APP STATE TO STORE THE TOASTS

const S = {};

S.FixedContainer = styled.div`
  position: fixed;
  bottom: 10px;
  /* right: 5px; */
  /* left: 0; right: 0; */
  /* CENTER IT HORIZONTALLY */
  left: 50%;
  transform: translateX(-50%);
`;

S.ToastContainer = styled.div`
  width: 300px;
  height: 64px;
  margin-top: 10px;
  margin-bottom: 10px;
  /* padding-left: 10px; */
  color: white;
  font-weight: bold;
  background: #39c16c;
  border-radius: 5px;
  display: flex;
  align-items: center;
  justify-content: center;
`;

function Toastr(props) {
  const lastToastLengthRef = useRef(0);
  const [toasts, setToasts] = useState([]);
  const toastID = useRef(0);

  console.log("Toastr rendering...");
  console.log(toasts);

  function addNewToast(toast) {
    setToasts(prevState => {
      const aux = Array.from(prevState);
      aux.push({ msg: toast, id: toastID.current });
      toastID.current = toastID.current + 1;
      return aux;
    });
  }

  useEffect(() => {
    if (toasts.length > lastToastLengthRef.current) {
      console.log("useEffect: Toast was added...");
      // TOAST WAS ADDED
      setTimeout(() => {
        setToasts(prevState => {
          const aux = Array.from(prevState);
          aux.shift();
          return aux;
        });
      }, 1000);
      lastToastLengthRef.current = toasts.length;
      return;
    }
    lastToastLengthRef.current = toasts.length;
  }, [toasts]);

  function animateEnter(element) {
    anime({
      targets: element,
      opacity: 0,
      duration: 0
    });
    anime({
      targets: element,
      opacity: 1,
      easing: "easeOutExpo",
      duration: 2000
    });
  }

  function animateExit(element) {
    anime({
      targets: element,
      opacity: 0,
      easing: "easeOutExpo",
      duration: 2000
    });
  }

  // const toastItems = toasts.map((item,index) =>
  //   <S.ToastContainer key={item.id}>{item.msg}</S.ToastContainer>
  // );

  const toastItems = toasts.map((item, index) => (
    <Transition
      key={item.id}
      onEntering={animateEnter}
      onExiting={animateExit}
      timeout={{
        enter: 500,
        exit: 500
      }}
      unmountOnExit={true}
    >
      <S.ToastContainer>{item.msg}</S.ToastContainer>
    </Transition>
  ));

  return (
    <React.Fragment>
      <S.FixedContainer>
        <TransitionGroup component={null}>{toastItems}</TransitionGroup>
        {/* {toastItems} */}
      </S.FixedContainer>
      <ToastrContext.Provider value={addNewToast}>
        {props.children}
      </ToastrContext.Provider>
    </React.Fragment>
  );
}

// Toastr.whyDidYouRender = true;

export default Toastr;

ToastrContext.js

import React from "react";

const ToastrContext = React.createContext(null);

export default ToastrContext;

SomeComponent.js(将发出烤面包)

import React, { useContext } from "react";
import ToastrContext from "./ToastrContext";
import styled from "styled-components";

function SomeComponent() {
  const sendToast = useContext(ToastrContext);

  return (
    <React.Fragment>
      <div>Hey! Click for some toasts!</div>
      <button onClick={() => sendToast("This is your toast!")}>Click</button>
    </React.Fragment>
  );
}

export default SomeComponent;