1、自定義hook
當我們想在兩個函數之間共享邏輯時, 我們會把它提取到第三個函數中,而組件和Hook都是函數,所以也同樣適用這種方式
自定義hook是一個函數, 其名稱是以use開頭, 函數內部可以調用其他的hook
import React, {useEffect, useState} from 'react'; import ReactDom from 'react-dom' const useTimer = () => { //注意:這里的函數是需要以use開頭 let [count, setCount] = useState(0) useEffect(() => { //相當於componentDidMount let timer = setInterval(() => { setCount(count => ++ count) }, 1000) return () => { //相當於componentWillUnmount clearInterval(timer) } }, []) return count } const CounterOne = () => { // let [count, setCount] = useState(0) // // useEffect(() => { // let timer = setInterval(() => { // setCount(count => ++ count) // }, 1000) // return () => { // clearInterval(timer) // } // }, []) let count = useTimer() return <h2>CounterOne ---{count}</h2> } const CounterTwo = () => { // let [count, setCount] = useState(0) // // useEffect(() => { // let timer = setInterval(() => { // setCount(count => ++ count) // }, 1000) // return () => { // clearInterval(timer) // } // }, []) let count = useTimer() return <h3>CounterTwo ---{count}</h3> } const App = () => { return <div> <CounterOne/> <CounterTwo/> </div> } ReactDom.render(<App/>, window.root)
原本useEffect與useState是不能放在函數里,如果放在use開頭的函數里,系統會認為是自定義的hook,這樣就可以對對應的方法進行封裝和使用
案例2: 對dispatch進行擴充,實現改變前與改變后值的打印
import React, {useEffect, useReducer} from 'react'; import ReactDom from 'react-dom' const useLogger = () => { const reducer = (state, config) => { if(config.type === 'count') { return { ...state, count: state.count + config.step } } else if(config.type === 'name') { return { ...state, name: config.target } } } let [state, dispatch] = useReducer(reducer, {name: 'yfbill', count: 0}) const actions = (config) => { //對原有dispatch進行擴充 console.log('before', state) dispatch(config) } useEffect(() => { console.log('after', state) }) return [state, actions] //輸出的擴充后的dispatch } const Item = () => { let [state, actions] = useLogger() return <div> <h3>this is Item---name: {state.name} --- count: {state.count}</h3> <button onClick={() => { actions({type: 'count', step: 1})}}>count++</button> <button onClick={() => { actions({type: 'name', target: 'aaaa'})}}>changeName</button> </div> } const App = () => { return <div> <h2>this is App</h2> <Item/> </div> } ReactDom.render(<App/>, window.root)
注意:以上這個嵌入式的寫法可以用作數據的請求,方便封裝操作
2、高階組件的寫法
import React from 'react'; import ReactDom from 'react-dom' const App = props => { console.log(props) return <div>this is App</div> } //扁平化的高階組件(推薦使用) // const Wrap = (config = {}, Comp) => { // return props => { // return <Comp {...config} {...props}/> // } // } //第二種方式的高階組件 const Wrap = (config = {}) => { return Comp => { return props => { return <Comp {...config} {...props}/> } } } // let Instance = Wrap({name: 'yfbill', age: 20}, App) let Instance = Wrap({name: 'yfbill', age: 20})(App) ReactDom.render(<Instance className="abc"/>, window.root)
通常來講,高階組件都是以with開頭,通常來講高階組件不會直接渲染到return的內容里面,需要賦值后再進行處理
const App: React.FC = () => { let Comp = configTag({name: 'yf', age: 10}) return <StyleApp> <LayoutHeader/> <Content><Comp/></Content> </StyleApp> }