前端時間,接觸了hooks,研究了一段時間後感受使用起來十分方便,正好公司開了一個新的小項目,正好使用hooks來實踐一下。前端
在以前的文章中我也介紹過umi的優勢,在使用過umi後,感受本身的開發效率有很大的提高。umi的路由使用起來實在是讓我愛不釋手,詳細的我就不過多介紹了,有興趣的能夠去看我以前的文章。react
TypeScript 是 Microsoft 開發和維護的一種面向對象的編程語言。它是 JavaScript 的超集,包含了 JavaScript 的全部元素,能夠載入 JavaScript 代碼運行,並擴展了 JavaScript 的語法。在使用TypeScript編程時,它能幫助你在寫代碼的過程當中考慮到各類類型上的問題,避免代碼運行時出現的意想不到的錯誤。使用了TypeScript能夠加強你代碼的健壯性,特別是大型項目的開發中,某些小小的改動都有可能對你的項目形成嚴重的後果。編程
其實使用React Hooks的目的就是爲了去redux,那我爲何還會使用基於redux-soga封裝的dva呢?緣由就在於hooks雖然很方便,但若是是一些很複雜的狀態須要去管理,這時候使用hooks就會有點兒費勁了。因此這時候結合dva來解決這種特別複雜的狀態管理是很方便的,原生的redux使用起來稍微有點兒麻煩,dva用起來很簡單,很爽,這就是我選擇dva的緣由。redux
這個是這篇文章的重點了,你在react開發過程當中有沒有碰見這三個問題。segmentfault
(一) 在組件組件複用狀態邏輯很難後端
React 沒有提供將可複用性行爲「附加」到組件的途徑(例如,把組件鏈接到 store)。若是你使用過 React 一段時間,你也許會熟悉一些解決此類問題的方案,好比 render props 和 高階組件。可是這類方案須要從新組織你的組件結構,這可能會很麻煩,使你的代碼難以理解。若是你在 React DevTools 中觀察過 React 應用,你會發現由 providers,consumers,高階組件,render props 等其餘抽象層組成的組件會造成「嵌套地獄」。儘管咱們能夠在 DevTools 過濾掉它們,但這說明了一個更深層次的問題:React 須要爲共享狀態邏輯提供更好的原生途徑。
(二) 複雜的組件變得難以理解數組
咱們常常維護一些組件,組件起初很簡單,可是逐漸會被狀態邏輯和反作用充斥。每一個生命週期經常包含一些不相關的邏輯。例如,組件經常在 componentDidMount 和 componentDidUpdate 中獲取數據。可是,同一個 componentDidMount 中可能也包含不少其它的邏輯,如設置事件監聽,而以後需在 componentWillUnmount 中清除。相互關聯且須要對照修改的代碼被進行了拆分,而徹底不相關的代碼卻在同一個方法中組合在一塊兒。如此很容易產生 bug,而且致使邏輯不一致。在多數狀況下,不可能將組件拆分爲更小的粒度,由於狀態邏輯無處不在。這也給測試帶來了必定挑戰。同時,這也是不少人將 React 與狀態管理庫結合使用的緣由之一。可是,這每每會引入了不少抽象概念,須要你在不一樣的文件之間來回切換,使得複用變得更加困難。
(三) 難以理解的class性能優化
除了代碼複用和代碼管理會遇到困難外,class 是學習 React 的一大屏障。你必須去理解 JavaScript 中 this 的工做方式,這與其餘語言存在巨大差別。還不能忘記綁定事件處理器。沒有穩定的語法提案,這些代碼很是冗餘。你們能夠很好地理解 props,state 和自頂向下的數據流,但對 class 卻束手無策。即使在有經驗的 React 開發者之間,對於函數組件與 class 組件的差別也存在分歧,甚至還要區分兩種組件的使用場景。
若是你也被這三種問題所困擾,這時候接觸hooks,你會發現打開了新世界的大門。從面向對象編程轉爲函數式編程,我感受釋放了本身,寫代碼變得又爽又飛快。antd
由於使用了umi,因此該項目也是用umi來搭建的,具體方法可查看以前文章。選擇ts版本,而後根據本身編程習慣,配置一下tslint規則就ok了。
這裏要注意一件重要的事情,升級react和react-dom的版本爲16.8.0以上,由於umi腳手架搭建的項目react版本爲16.7.0,而16.8.0是react正式支持hook的版本。dom
首先咱們來看一下官方的代碼。
import React, { useState } from 'react'; function Example() { // 聲明一個新的叫作 「count」 的 state 變量 const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
使用起來十分簡單,函數式編程讓你不用再繼承Component,直接定義一個變量就行,能夠給這個變量看作你以前寫react中state的一個值。使用Hooks以後你不用再調用setState來更改state中的值,可使用你本身定義的更改方法,上面代碼中就是使用setCount來更改count的值,是否是很方便?
怎麼在hooks中使用TypeScript?
若是你使用Component來編寫你的組件,你須要經過interface來定義你state中值的類型,使用Hooks,你只須要這樣:
const [count, setCount] = useState<number>(0)
Effect Hook 可讓你在函數組件中執行反作用操做,下面是官方代碼。
import React, { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
這段代碼中使用了useEffect,可讓你在屏幕中顯示你點擊的次數。useEffect最大的好處我認爲就是去生命週期鉤子函數了,使用以後你不用再使用任何生命週期鉤子函數,這一點來看是否是就特別爽?useEffect爲何會實現去生命週期鉤子的做用呢?你能夠在函數中寫一個console.log,查看控制檯後你便會發現控制檯會一直打印你的console.log,因此useEffect會在組件的生命週期中一直被調用,咱們在使用的時候能夠告訴useEffect何時纔會被調用來進行性能優化。好比上面代碼咱們能夠這樣修改:
useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; },[count])
這樣修改後useEffect會在count的值發生改變後才被調用。
在開發過程當中咱們可能會使用到定時器,而異步隊列中的定時器在組件被銷燬後也會繼續執行,這樣便會形成內存泄漏,在Component中咱們會調用componentWillUnmount函數來清除定時器,在useEffect中咱們該怎麼辦呢?
import React, { useState, useEffect } from 'react' import moment from 'moment' export default function () { const [nowTime, setNowTime] = useState(moment().format('YYYY年MM月DD日 ddd HH:mm')) useEffect(() => { const timer = setInterval(() => { setNowTime(moment().format('YYYY年MM月DD日 ddd HH:mm:ss')) }, 1000); return () => { clearInterval(timer); } },[nowTime]) return ( <>{nowTime}</> ) }
上面代碼就是一段很簡單的顯示當前時間的代碼,咱們能夠經過return函數在組件銷燬的時候清除useEffect中的定時器
從後端獲取數據,而後在表格中渲染應該是很常見的一個功能了,下面咱們來看一下使用Hooks以後怎麼寫這種組件
import React, { useState, useEffect } from 'react' import { Table } from 'antd' export default function () { const [tableData, setTableData] = useState<any[]>([]) const [page, setPage] = useState<number>(1) const [pageSize, setPageSize] = useState<number>(15) const [count, setCount] = useState<number>(0) const [loading,setLoading] = useState<boolean>(true) useEffect(() => { fetch('www.baidu.com').then( function(data) { if(data) { setTableData(data.result) setCount(data.count) setLoading(false) } }) },[page,pageSize]) const onChangePage = (pageNumber: any) => setPage(pageNumber) const onChangePageSize = (value: number) => setPageSize(value) const columns = [ { title: 'ID', dataIndex: 'id' }, { title: '姓名', dataIndex: 'name' }, { title: '電話', dataIndex: 'tel', }, { title: '性別', dataIndex: 'gender', }, { title: '年齡', dataIndex: 'age', }, ] return ( <> <Table bordered={false} rowKey={(r, i) => (i + '')} columns={columns} loading={loading} dataSource={tableData.length ? tableData : []} pagination={false} style={{ marginTop: 10 }} size="small" /> <Row> <Col span={8}> <span>搜索到{props.count}條數據</span> <span style={{ margin: '0 20px' }}>共{Math.ceil(count / pageSize)}頁</span> <Select defaultValue={15} onChange={onChangePageSize}> <Option value={10}>10條/頁</Option> <Option value={15}>15條/頁</Option> <Option value={20}>20條/頁</Option> <Option value={30}>30條/頁</Option> </Select> </Col> <Col span={14} push={2} style={{display:'flex', justifyContent:'flex-end', marginRight: 10}}> <Pagination showQuickJumper={true} current={page} pageSize={pageSize} total={count} onChange={onChangePage} /> </Col> </Row> </> ) }
這樣就寫完一個功能完善的表格組件了,回想一下你以前是用Component面向對象編程時怎麼寫的,再看一下用Hooks函數式編程的代碼是否是簡單不少?趕忙來試試hooks吧,會讓你有寫代碼停不下來的感受。