原文: www.freecodecamp.org/news...
译文:前端技术小哥css
Restration Hooks取悦了开发人员。但对我来讲,我已经开始对Hooks 感到疲劳。 React Hooks的示例,而不只仅是“新方法”。正如你们根据本文的标题所猜想的,这个示例是一个动画。但机缘巧合下我改变了个人见解。 我正在开发一个使用网格中的卡片的阵营应用程序。当一个项目被删除时,我想让它的退出动画化,就像这样。html
不巧的是,这其实和我预计的有些许细微差异。而个人解决方案让我很好地利用了React Hooks。前端
咱们要作什么?
从一个基线示例应用程序开始
逐渐增长消失的动画元素,把难点挑出来重点处理
一旦咱们实现了所需的动画,咱们将重构一个可重用的动画组件
咱们将使用此组件使侧边栏和导航栏具备动画效果
以及...(你须要读/跳到最后)react
对于没有耐心的人,这里是这个项目的代码的GitHub repo。每一个步骤都有标签。(有关每一个标记的连接和描述,请参见自述)。ajax
基线
我用建立反应的应用内建立了一个简单的应用程序。它有一个简单的卡片网格。咱们能够隐藏单独的卡片。npm
(没有动画 - 卡片消失得不连贯)编程
这个的代码是很基础的,结果也是无趣的。当用户单击眼图标按钮时,会咱们更改卡片的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 (
<div className="box" style={style}>
{" "}
<div className="center">{word}</div>{" "}
<button className="button bottom-corner" onClick={hideMe}>
{" "}
<i className="center far fa-eye fa-lg" />{" "}
</button>{" "}
</div>
);
}
复制代码
(是的,在上面我使用了鱼钩,但这个用法并不有趣。)bash
添加动画
我没有创建本身的动画库,而是寻找相似animate.css的动画库.React动画-CSS是一个很棒的动画库,它提供了一个围绕animate.css的包装。
npm install --save react-animated-css
把animate.css添加到index.html的app
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.css" />
复制代码
上面在的Box组件中,咱们将其渲染更改成
return (
<Animated animationIn="zoomIn" animationOut="zoomOut" isVisible={visible}>
<div className="box" style={style}>
<div className="center">{word}</div>
<button className="button bottom-corner" onClick={hideMe}>
<i className="center far fa-eye fa-lg" />
</button>
</div>
</Animated>
);
复制代码
和咱们中预期的不相符 可是animate.css设置能够opacity状语从句:其余的CSS属性的动画;不能咱们在display属性上进行CSS转换所以,一个不可见的对象仍然存在,它占用了文档流中的空间。
因此咱们能够补充一点
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 (
<Animated
animationIn="zoomIn"
animationOut="zoomOut"
isVisible={!fading}
style={visible ? null : { display: "none" }}
>
<div className="box" style={style}>
<div className="center">{word}</div>
<button className="button bottom-corner" onClick={hideMe}>
<i className="center far fa-eye fa-lg" />
</button>
</div>
</Animated>
);
}
复制代码
(注意:默认动画持续时间为1000毫秒我使用650毫秒做为超时,以便在设置display。属性以前最小化停滞感这是一个优先考虑的问题)
这将给咱们带来预期中的效果。
(奈思!)
建立可复用组件
咱们能够在这里停下来,但有两个问题(对我来讲):
一、我不想复制/粘贴Animated块,样式和函数来从新建立此效果 二、Box组件混合了不一样类型的逻辑,即违反了关注点分离。具体来讲,Box的基本功能是使用卡片呈现其内容。但动画细目混杂在了一块儿。
类组件
咱们能够建立一个传统的阵营类组件来管理动画状态:切换可见性并设置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 (
<Animated
animationIn="zoomIn"
animationOut="zoomOut"
isVisible={this.state.visible}
style={this.state.noDisplay ? { display: "none" } : null}
>
{this.props.children}
</Animated>
);
}
}
复制代码
而后用于
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 (
<AnimatedVisibility visible={visible}>
<div className="box" style={style}>
<div className="center">{word}</div>
<button className="button bottom-corner" onClick={hideMe}>
<i className="center far fa-eye fa-lg" />
</button>
</div>
</AnimatedVisibility>
);
}
复制代码
这的确建立了一个可复用组件,但它有点过于复杂。咱们本能够作得更好。
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 (
<Animated
animationIn="zoomIn"
animationOut="zoomOut"
isVisible={visible}
style={style}
>
{children}
</Animated>
);
}
复制代码
使用useEffect hook有一些微妙之处。它主要用于反作用:更改状态,调用异步函数等。在咱们的示例中,它根据先前的值可见设置内部noDisplay布尔值。经过向依赖关系数组的useEffect中添加visible,咱们的useEffect 挂钩只会在visible值发生更改时调用。
我认为useEffect是一个比类组件杂波更好的解决方案,大家以为呢?复用组件:侧边栏和导航栏
你们都喜欢侧边栏和导航栏。那咱们分别加一个吧。
function ToggleButton({ label, isOpen, onClick }) {
const icon = isOpen ? (
<i className="fas fa-toggle-off fa-lg" />
) : (
<i className="fas fa-toggle-on fa-lg" />
);
return (
<button className="toggle" onClick={onClick}>
{label} {icon}
</button>
);
}
function Navbar({ open }) {
return (
<AnimatedVisibility
visible={open}
animationIn="slideInDown"
animationOut="slideOutUp"
animationInDuration={300}
animationOutDuration={600}
>
<nav className="bar nav">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</nav>
</AnimatedVisibility>
);
}
function Sidebar({ open }) {
return (
<AnimatedVisibility
visible={open}
animationIn="slideInLeft"
animationOut="slideOutLeft"
animationInDuration={500}
animationOutDuration={600}
className="on-top"
>
<div className="sidebar">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
</AnimatedVisibility>
);
}
function App() {
const [navIsOpen, setNavOpen] = useState(false);
const [sidebarIsOpen, setSidebarOpen] = useState(false);
function toggleNav() {
setNavOpen(!navIsOpen);
}
function toggleSidebar() {
setSidebarOpen(!sidebarIsOpen);
}
return (
<Fragment>
<main className="main">
<header className="bar header">
<ToggleButton
label="Sidebar"
isOpen={sidebarIsOpen}
onClick={toggleSidebar}
/>
<ToggleButton label="Navbar" isOpen={navIsOpen} onClick={toggleNav} />
</header>
<Navbar open={navIsOpen} />
<Boxes />
</main>
<Sidebar open={sidebarIsOpen} />
</Fragment>
);
}
复制代码
(达成复用)
但咱们尚未结束......
咱们能够在这里停下来。但正如我以前关于分离关注的评论同样,我宁愿避免在Box,Sidebar和Navbar的渲染方法中混合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 (
<AnimatedVisibility
visible={open}
animationIn={animationIn}
animationOut={animationOut}
animationInDuration={animationInDuration}
animationOutDuration={animationOutDuration}
disappearOffset={disappearOffset}
className={className}
>
<Component {...props} />
</AnimatedVisibility>
);
};
}
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 (
<nav className="bar nav">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</nav>
);
}
function Sidebar() {
return (
<div className="sidebar">
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
);
}
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 (
<Fragment>
<main className="main">
<header className="bar header">
<ToggleButton
label="Sidebar"
isOpen={sidebarIsOpen}
onClick={toggleSidebar}
/>
<ToggleButton label="Navbar" isOpen={navIsOpen} onClick={toggleNav} />
</header>
<AnimatedNavbar open={navIsOpen} />
<Boxes />
</main>
<AnimatedSidebar open={sidebarIsOpen} className="on-top"/>
</Fragment>
);
}
复制代码
比起冒着推广本身做品的风险,我更喜欢干净的结果代码。下面是最终结果的沙盒。(此处更改)
我建议你们能够查看像useHooks.com这样的网站和像反应使用这样的库,这是一个用于各类各样的用例的钩集合。
但愿本文能帮助到您!
看以后
点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓-_-)
关注公众号「新前端社区」,享受文章首发体验!
每周重点攻克一个前端技术难点。