原文: 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這樣的網站和像反應使用這樣的庫,這是一個用於各類各樣的用例的鉤集合。
但願本文能幫助到您!
看以後
點贊,讓更多的人也能看到這篇內容(收藏不點贊,都是耍流氓-_-)
關注公衆號「新前端社區」,享受文章首發體驗!
每週重點攻克一個前端技術難點。