<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## 1. React Hooks * Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性 * 如果你在編寫函數組件并意識到需要向其添加一些 state,以前的做法是必須將其它轉化為 class。現在你可以在現有的函數組件中使用 Hook ## 2. 解決的問題 * 在組件之間復用狀態邏輯很難,可能要用到render props和高階組件,React 需要為共享狀態邏輯提供更好的原生途徑,Hook 使你在無需修改組件結構的情況下復用狀態邏輯 * 復雜組件變得難以理解,Hook 將組件中相互關聯的部分拆分成更小的函數(比如設置訂閱或請求數據) * 難以理解的 class,包括難以捉摸的`this` ## 3. 注意事項 * 只能在函數最外層調用 Hook。不要在循環、條件判斷或者子函數中調用。 * 只能在 React 的函數組件中調用 Hook。不要在其他 JavaScript 函數中調用 ## 4\. useState * useState 就是一個 Hook * 通過在函數組件里調用它來給組件添加一些內部 state,React 會在重復渲染時保留這個 state * useState 會返回一對值:當前狀態和一個讓你更新它的函數,你可以在事件處理函數中或其他一些地方調用這個函數。它類似 class 組件的 this.setState,但是它不會把新的 state 和舊的 state 進行合并 * useState 唯一的參數就是初始 state * 返回一個 state,以及更新 state 的函數 * 在初始渲染期間,返回的狀態 (state) 與傳入的第一個參數 (initialState) 值相同 * setState 函數用于更新 state。它接收一個新的 state 值并將組件的一次重新渲染加入隊列 ~~~ const [state, setState] = useState(initialState); ~~~ ### 4.1 計數器 ~~~ import React,{useState} from 'react'; class Counter extends React.Component { constructor(props) { super(props); this.state = { number: 0 }; } render() { return ( <div> <p>{this.state.number}</p> <button onClick={() => this.setState({ number: this.state.number + 1 })}> + </button> </div> ); } } function Counter2(){ const [number,setNumber] = useState(0); return ( <> <p>{number}</p> <button onClick={()=>setNumber(number+1)}>+</button> </> ) } export default Counter2; ~~~ ### 4.2 每次渲染都是獨立的閉包 * 每一次渲染都有它自己的 Props and State * 每一次渲染都有它自己的事件處理函數 * alert會“捕獲”我點擊按鈕時候的狀態。 * 我們的組件函數每次渲染都會被調用,但是每一次調用中number值都是常量,并且它被賦予了當前渲染中的狀態值 * 在單次渲染的范圍內,props和state始終保持不變 * [making-setinterval-declarative-with-react-hooks](https://overreacted.io/making-setinterval-declarative-with-react-hooks/) ~~~ function Counter2(){ const [number,setNumber] = useState(0); function alertNumber(){ setTimeout(()=>{ alert(number); },3000); } return ( <> <p>{number}</p> <button onClick={()=>setNumber(number+1)}>+</button> <button onClick={alertNumber}>alertNumber</button> </> ) } ~~~ ~~~ function Counter() { const [number, setNumber] = useState(0); const savedCallback = useRef(); function alertNumber() { setTimeout(() => { alert(savedCallback.current); }, 3000); } return ( <> <p>{number}</p> <button onClick={() => { setNumber(number + 1); savedCallback.current = number + 1; }}>+</button> <button onClick={alertNumber}>alertNumber</button> </> ) } ~~~ ### 4.3 函數式更新 * 如果新的 state 需要通過使用先前的 state 計算得出,那么可以將函數傳遞給 setState。該函數將接收先前的 state,并返回一個更新后的值 ~~~ function Counter2(){ const [number,setNumber] = useState(0); let numberRef = useRef(number); numberRef.current = number; function alertNumber(){ setTimeout(()=>{ alert(numberRef.current); },3000); } + function lazy(){ + setTimeout(()=>{ + setNumber(number+1); + },3000); + } + function lazyFunc(){ + setTimeout(()=>{ + setNumber(number=>number+1); + },3000); + } return ( <> <p>{number}</p> <button onClick={()=>setNumber(number+1)}>+</button> <button onClick={lazy}>lazy+</button> <button onClick={lazyFunc}>lazyFunc+</button> <button onClick={alertNumber}>alertNumber</button> </> ) } ~~~ ### 4.4 惰性初始 state * initialState 參數只會在組件的初始渲染中起作用,后續渲染時會被忽略 * 如果初始 state 需要通過復雜計算獲得,則可以傳入一個函數,在函數中計算并返回初始的 state,此函數只在初始渲染時被調用 * 與 class 組件中的 setState 方法不同,useState 不會自動合并更新對象。你可以用函數式的 setState 結合展開運算符來達到合并更新對象的效果 ~~~ function Counter3(){ const [{name,number},setValue] = useState(()=>{ return {name:'計數器',number:0}; }); return ( <> <p>{name}:{number}</p> <button onClick={()=>setValue({number:number+1})}>+</button> </> ) } ~~~ ### 4.5 性能優化 #### 4.5.1 Object.is * 調用 State Hook 的更新函數并傳入當前的 state 時,React 將跳過子組件的渲染及 effect 的執行。(React 使用 Object.is 比較算法 來比較 state。) ~~~ function Counter4(){ const [counter,setCounter] = useState({name:'計數器',number:0}); console.log('render Counter') return ( <> <p>{counter.name}:{counter.number}</p> <button onClick={()=>setCounter({...counter,number:counter.number+1})}>+</button> <button onClick={()=>setCounter(counter)}>-</button> </> ) } ~~~ #### 4.5.2 減少渲染次數 * 把內聯回調函數及依賴項數組作為參數傳入`useCallback`,它將返回該回調函數的 memoized 版本,該回調函數僅在某個依賴項改變時才會更新 * 把創建函數和依賴項數組作為參數傳入`useMemo`,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優化有助于避免在每次渲染時都進行高開銷的計算 ~~~ function Child({onButtonClick,data}){ console.log('Child render'); return ( <button onClick={onButtonClick} >{data.number}</button> ) } Child = memo(Child); function App(){ const [number,setNumber] = useState(0); const [name,setName] = useState('zhufeng'); const addClick = useCallback(()=>setNumber(number+1),[number]); const data = useMemo(()=>({number}),[number]); return ( <div> <input type="text" value={name} onChange={e=>setName(e.target.value)}/> <Child onButtonClick={addClick} data={data}/> </div> ) } ~~~ ### 4.6 注意事項 * 只能在函數最外層調用 Hook。不要在循環、條件判斷或者子函數中調用。 ~~~ import React, { useEffect, useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; function App() { const [number, setNumber] = useState(0); const [visible, setVisible] = useState(false); if (number % 2 == 0) { useEffect(() => { setVisible(true); }, [number]); } else { useEffect(() => { setVisible(false); }, [number]); } return ( <div> <p>{number}</p> <p>{visible && <div>visible</div>}</p> <button onClick={() => setNumber(number + 1)}>+</button> </div> ) } ReactDOM.render(<App />, document.getElementById('root')); ~~~ ## 5\. useReducer * useState 的替代方案。它接收一個形如 (state, action) => newState 的 reducer,并返回當前的 state 以及與其配套的 dispatch 方法 * 在某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較復雜且包含多個子值,或者下一個 state 依賴于之前的 state 等 ### 5.1 基本用法 ~~~ const [state, dispatch] = useReducer(reducer, initialArg, init); ~~~ ~~~ const initialState = 0; function reducer(state, action) { switch (action.type) { case 'increment': return {number: state.number + 1}; case 'decrement': return {number: state.number - 1}; default: throw new Error(); } } function init(initialState){ return {number:initialState}; } function Counter(){ const [state, dispatch] = useReducer(reducer, initialState,init); return ( <> Count: {state.number} <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ) } ~~~ ## 6\. useContext * 接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當前值 * 當前的 context 值由上層組件中距離當前組件最近的 的 value prop 決定 * 當組件上層最近的 更新時,該 Hook 會觸發重渲染,并使用最新傳遞給 MyContext provider 的 context value 值 * useContext(MyContext) 相當于 class 組件中的`static contextType = MyContext`或者`<MyContext.Consumer>` * useContext(MyContext) 只是讓你能夠讀取 context 的值以及訂閱 context 的變化。你仍然需要在上層組件樹中使用 來為下層組件提供 context ~~~ const CounterContext = React.createContext(); function reducer(state, action) { switch (action.type) { case 'increment': return {number: state.number + 1}; case 'decrement': return {number: state.number - 1}; default: throw new Error(); } } function Counter(){ let {state,dispatch} = useContext(CounterContext); return ( <> <p>{state.number}</p> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ) } function App(){ const [state, dispatch] = useReducer(reducer, {number:0}); return ( <CounterContext.Provider value={{state,dispatch}}> <Counter/> </CounterContext.Provider> ) } ~~~ ## 7\. effect * 在函數組件主體內(這里指在 React 渲染階段)改變 DOM、添加訂閱、設置定時器、記錄日志以及執行其他包含副作用的操作都是不被允許的,因為這可能會產生莫名其妙的 bug 并破壞 UI 的一致性 * 使用 useEffect 完成副作用操作。賦值給 useEffect 的函數會在組件渲染到屏幕之后執行。你可以把 effect 看作從 React 的純函數式世界通往命令式世界的逃生通道 * useEffect 就是一個 Effect Hook,給函數組件增加了操作副作用的能力。它跟 class 組件中的`componentDidMount`、`componentDidUpdate`和`componentWillUnmount`具有相同的用途,只不過被合并成了一個 API * 該 Hook 接收一個包含命令式、且可能有副作用代碼的函數 ~~~ useEffect(didUpdate); ~~~ ### 7.1 通過class實現修標題 ~~~ class Counter extends React.Component { constructor(props) { super(props); this.state = { number: 0 }; } componentDidMount() { document.title = `你點擊了${this.state.number}次`; } componentDidUpdate() { document.title = `你點擊了${this.state.number}次`; } render() { return ( <div> <p>{this.state.number}</p> <button onClick={() => this.setState({ number: this.state.number + 1 })}> + </button> </div> ); } } ~~~ > 在這個 class 中,我們需要在兩個生命周期函數中編寫重復的代碼,這是因為很多情況下,我們希望在組件加載和更新時執行同樣的操作。我們希望它在每次渲染之后執行,但 React 的 class 組件沒有提供這樣的方法。即使我們提取出一個方法,我們還是要在兩個地方調用它。useEffect會在第一次渲染之后和每次更新之后都會執行 ### 7.2 通過effect實現 ~~~ import React,{Component,useState,useEffect} from 'react'; import ReactDOM from 'react-dom'; function Counter(){ const [number,setNumber] = useState(0); // 相當于 componentDidMount 和 componentDidUpdate: useEffect(() => { // 使用瀏覽器的 API 更新頁面標題 document.title = `你點擊了${number}次`; }); return ( <> <p>{number}</p> <button onClick={()=>setNumber(number+1)}>+</button> </> ) } ReactDOM.render(<Counter />, document.getElementById('root')); ~~~ > 每次我們重新渲染,都會生成新的 effect,替換掉之前的。某種意義上講,effect 更像是渲染結果的一部分 —— 每個 effect 屬于一次特定的渲染。 ### 7.3 跳過 Effect 進行性能優化 * 如果某些特定值在兩次重渲染之間沒有發生變化,你可以通知 React 跳過對 effect 的調用,只要傳遞數組作為 useEffect 的第二個可選參數即可 * 如果想執行只運行一次的 effect(僅在組件掛載和卸載時執行),可以傳遞一個空數組(\[\])作為第二個參數。這就告訴 React 你的 effect 不依賴于 props 或 state 中的任何值,所以它永遠都不需要重復執行 ~~~ function Counter(){ const [number,setNumber] = useState(0); // 相當于componentDidMount 和 componentDidUpdate useEffect(() => { console.log('開啟一個新的定時器') const $timer = setInterval(()=>{ setNumber(number=>number+1); },1000); },[]); return ( <> <p>{number}</p> </> ) } ~~~ ### 7.4 清除副作用 * 副作用函數還可以通過返回一個函數來指定如何清除副作用 * 為防止內存泄漏,清除函數會在組件卸載前執行。另外,如果組件多次渲染,則在執行下一個 effect 之前,上一個 effect 就已被清除 ~~~ import React, { useEffect, useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; function Counter() { const [number, setNumber] = useState(0); useEffect(() => { console.log('開啟一個新的定時器') const $timer = setInterval(() => { setNumber(number => number + 1); }, 1000); return () => { console.log('銷毀老的定時器'); clearInterval($timer); } }); return ( <> <p>{number}</p> </> ) } function App() { let [visible, setVisible] = useState(true); return ( <div> {visible && <Counter />} <button onClick={() => setVisible(false)}>stop</button> </div> ) } ReactDOM.render(<App />, document.getElementById('root')); ~~~ ### 7.5 useRef * useRef 返回一個可變的 ref 對象,其`.current`屬性被初始化為傳入的參數(initialValue) * 返回的 ref 對象在組件的整個生命周期內保持不變 ~~~ const refContainer = useRef(initialValue); ~~~ #### 7.5.1 useRef ~~~ import React, { useState, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; function Parent() { let [number, setNumber] = useState(0); return ( <> <Child /> <button onClick={() => setNumber({ number: number + 1 })}>+</button> </> ) } let input; function Child() { const inputRef = useRef(); console.log('input===inputRef', input === inputRef); input = inputRef; function getFocus() { inputRef.current.focus(); } return ( <> <input type="text" ref={inputRef} /> <button onClick={getFocus}>獲得焦點</button> </> ) } ReactDOM.render(<Parent />, document.getElementById('root')); ~~~ #### 7.5.2 forwardRef * 將ref從父組件中轉發到子組件中的dom元素上 * 子組件接受props和ref作為參數 ~~~ function Child(props,ref){ return ( <input type="text" ref={ref}/> ) } Child = forwardRef(Child); function Parent(){ let [number,setNumber] = useState(0); const inputRef = useRef(); function getFocus(){ inputRef.current.value = 'focus'; inputRef.current.focus(); } return ( <> <Child ref={inputRef}/> <button onClick={()=>setNumber({number:number+1})}>+</button> <button onClick={getFocus}>獲得焦點</button> </> ) } ~~~ #### 7.5.3 useImperativeHandle * `useImperativeHandle`可以讓你在使用 ref 時自定義暴露給父組件的實例值 * 在大多數情況下,應當避免使用 ref 這樣的命令式代碼。useImperativeHandle 應當與 forwardRef 一起使用 ~~~ function Child(props,ref){ const inputRef = useRef(); useImperativeHandle(ref,()=>( { focus(){ inputRef.current.focus(); } } )); return ( <input type="text" ref={inputRef}/> ) } Child = forwardRef(Child); function Parent(){ let [number,setNumber] = useState(0); const inputRef = useRef(); function getFocus(){ console.log(inputRef.current); inputRef.current.value = 'focus'; inputRef.current.focus(); } return ( <> <Child ref={inputRef}/> <button onClick={()=>setNumber({number:number+1})}>+</button> <button onClick={getFocus}>獲得焦點</button> </> ) } ~~~ ## 8\. useLayoutEffect * 其函數簽名與 useEffect 相同,但它會在所有的 DOM 變更之后同步調用 effect * 可以使用它來讀取 DOM 布局并同步觸發重渲染 * 在瀏覽器執行繪制之前useLayoutEffect內部的更新計劃將被同步刷新 * 盡可能使用標準的 useEffect 以避免阻塞視圖更新 ![domrender](http://img.zhufengpeixun.cn/domrender.jpg) ~~~ function LayoutEffect() { const [color, setColor] = useState('red'); useLayoutEffect(() => { alert(color); }); useEffect(() => { console.log('color', color); }); return ( <> <div id="myDiv" style={{ background: color }}>顏色</div> <button onClick={() => setColor('red')}>紅</button> <button onClick={() => setColor('yellow')}>黃</button> <button onClick={() => setColor('blue')}>藍</button> </> ); } ~~~ ## 9\. 自定義 Hook * 有時候我們會想要在組件之間重用一些狀態邏輯 * 自定義 Hook 可以讓你在不增加組件的情況下達到同樣的目的 * Hook 是一種復用狀態邏輯的方式,它不復用 state 本身 * 事實上 Hook 的每次調用都有一個完全獨立的 state * 自定義 Hook 更像是一種約定,而不是一種功能。如果函數的名字以 use 開頭,并且調用了其他的 Hook,則就稱其為一個自定義 Hook ### 9.1.自定義計數器 ~~~ function useNumber(){ const [number,setNumber] = useState(0); useEffect(() => { console.log('開啟一個新的定時器') const $timer = setInterval(()=>{ setNumber(number+1); },1000); return ()=>{ console.log('銷毀老的定時器') clearInterval($timer); } }); return number; } function Counter1(){ let number1 = useNumber(); return ( <> <p>{number1}</p> </> ) } function Counter2(){ let number = useNumber(); return ( <> <p>{number}</p> </> ) } function App(){ return <><Counter1/><Counter2/></> } ~~~ ### 9.2 中間件 #### 9.2.1 logger ~~~ import React, { useEffect, useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; const initialState = 0; function reducer(state, action) { switch (action.type) { case 'increment': return { number: state.number + 1 }; case 'decrement': return { number: state.number - 1 }; default: throw new Error(); } } function init(initialState) { return { number: initialState }; } function useLogger(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchWithLogger = (action) => { console.log('老狀態', state); dispatch(action); } useEffect(function () { console.log('新狀態', state); }, [state]); return [state, dispatchWithLogger]; } function Counter() { const [state, dispatch] = useLogger(reducer, initialState, init); return ( <> Count: {state.number} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'decrement' })}>-</button> </> ) } ReactDOM.render(<Counter />, document.getElementById('root')); ~~~ #### 9.2.2 promise ~~~ import React, { useEffect, useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; const initialState = 0; function reducer(state, action) { switch (action.type) { case 'increment': return { number: state.number + 1 }; case 'decrement': return { number: state.number - 1 }; default: throw new Error(); } } function init(initialState) { return { number: initialState }; } function useLogger(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchWithLogger = (action) => { console.log('老狀態', state); dispatch(action); } useEffect(function () { console.log('新狀態', state); }, [state]); return [state, dispatchWithLogger]; } function usePromise(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchPromise = (action) => { if (action.payload && action.payload.then) { action.payload.then((payload) => dispatch({ ...action, payload })); } else { dispatch(action); } } return [state, dispatchPromise]; } function Counter() { const [state, dispatch] = usePromise(reducer, initialState, init); return ( <> Count: {state.number} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch({ type: 'increment', payload: new Promise(resolve => { setTimeout(resolve, 1000); }) })}>delay</button> </> ) } ReactDOM.render(<Counter />, document.getElementById('root')); ~~~ #### 9.2.3 thunk ~~~ import React, { useEffect, useState, useReducer } from 'react'; import ReactDOM from 'react-dom'; import { resolve } from 'dns'; const initialState = 0; function reducer(state, action) { switch (action.type) { case 'increment': return { number: state.number + 1 }; case 'decrement': return { number: state.number - 1 }; default: throw new Error(); } } function init(initialState) { return { number: initialState }; } function useLogger(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchWithLogger = (action) => { console.log('老狀態', state); dispatch(action); } useEffect(function () { console.log('新狀態', state); }, [state]); return [state, dispatchWithLogger]; } function usePromise(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchPromise = (action) => { if (action.payload && action.payload.then) { action.payload.then((payload) => dispatch({ ...action, payload })); } else { dispatch(action); } } return [state, dispatchPromise]; } function useThunk(reducer, initialState, init) { const [state, dispatch] = useReducer(reducer, initialState, init); let dispatchPromise = (action) => { if (typeof action === 'function') { action(dispatchPromise, () => state); } else { dispatch(action) } } return [state, dispatchPromise]; } function Counter() { const [state, dispatch] = useThunk(reducer, initialState, init); return ( <> Count: {state.number} <button onClick={() => dispatch({ type: 'increment' })}>+</button> <button onClick={() => dispatch(function (dispatch, getState) { setTimeout(function () { dispatch({ type: 'increment' }); }, 1000); })}>delay</button> </> ) } ReactDOM.render(<Counter />, document.getElementById('root')); ~~~ ### 9.3 ajax ~~~ import React, { useState, useEffect, useLayoutEffect } from 'react'; import ReactDOM from 'react-dom'; function useRequest(url) { let limit = 5; let [offset, setOffset] = useState(0); let [data, setData] = useState([]); function loadMore() { setData(null); fetch(`${url}?offset=${offset}&limit=${limit}`) .then(response => response.json()) .then(pageData => { setData([...data, ...pageData]); setOffset(offset + pageData.length); }); } useEffect(loadMore, []); return [data, loadMore]; } function App() { const [users, loadMore] = useRequest('http://localhost:8000/api/users'); if (users === null) { return <div>正在加載中....</div> } return ( <> <ul> { users.map((item, index) => <li key={index}>{item.id}:{item.name}</li>) } </ul> <button onClick={loadMore}>加載更多</button> </> ) } ReactDOM.render(<App />, document.getElementById('root')); ~~~ async+await ~~~ import React, { useState, useEffect, useLayoutEffect } from 'react'; import ReactDOM from 'react-dom'; function useRequest(url) { let limit = 5; let [offset, setOffset] = useState(0); let [data, setData] = useState([]); async function loadMore() { setData(null); let pageData = await fetch(`${url}?offset=${offset}&limit=${limit}`) .then(response => response.json()); setData([...data, ...pageData]); setOffset(offset + pageData.length); } useEffect(loadMore, []); return [data, loadMore]; } function App() { const [users, loadMore] = useRequest('http://localhost:8000/api/users'); if (users === null) { return <div>正在加載中....</div> } return ( <> <ul> { users.map((item, index) => <li key={index}>{item.id}:{item.name}</li>) } </ul> <button onClick={loadMore}>加載更多</button> </> ) } ReactDOM.render(<App />, document.getElementById('root')); ~~~ ~~~ let express = require('express'); let app = express(); app.use(function (req, res, next) { res.header('Access-Control-Allow-Origin', 'http://localhost:3000'); next(); }); app.get('/api/users', function (req, res) { let offset = parseInt(req.query.offset); let limit = parseInt(req.query.limit); let result = []; for (let i = offset; i < offset + limit; i++) { result.push({ id: i + 1, name: 'name' + (i + 1) }); } res.json(result); }); app.listen(8000); ~~~ ### 9.4 動畫 ~~~ import React, { useState, useEffect, useLayoutEffect } from 'react'; import ReactDOM from 'react-dom'; import './index.css'; function useMove(initialClassName) { const [className, setClassName] = useState(initialClassName); const [state, setState] = useState(''); function start() { setState('bigger'); } useEffect(() => { if (state === 'bigger') { setClassName(`${initialClassName} ${initialClassName}-bigger`); } }, [state]); return [className, start]; } function App() { const [className, start] = useMove('circle'); return ( <div> <button onClick={start}>start</button> <div className={className}></div> </div> ) } ReactDOM.render(<App />, document.getElementById('root')); ~~~ ~~~ .circle { width : 50px; height : 50px; border-radius: 50%; background : red; transition: all .5s; } .circle-bigger { width : 200px; height : 200px; } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看