React v16.7.0-alpha中加入的新特性Hooks。它可以讓你在函式式元件中使用state和生命週期。本人花了點時間研究了下,發現網上大部分的教程都是偏向於理論,或者乾脆翻譯官網和程式碼,缺少實際使用場景的教程,所以本人根據自己的理解寫了這篇從開發角度去看這個hooks新特性(高手勿拍)
前言
理論方面的我不會講太多,大家可以搜一搜,非常多的文章。或者直接去看官網。我主要會講我在專案開發中會怎麼使用它。
hooks是幹什麼的
簡單的來說,就是讓你的函式式無狀態元件,支援使用狀態元件的state和生命週期。另外可以解決this繫結這個麻煩的東西。(程式碼量也會減少很多)
常用的都有什麼
相對我開發中常用的hooks大致有useState、useReducer、useEffect。另外還有一些我自定義的hooks。
直接開始用hooks
接下來我們直接開始用,跳過令人頭疼的理論,直接在使用中理解他們到底是幹什麼的。
首先我們需要一個基於react的開發框架,這裡打個小廣告,推薦自己前段時間分享的一篇文章《如何搭建一個REACT全家桶框架》。
在原先的userInfo頁面裡面進行一個常規查詢列表功能的編寫。不用hooks功能的寫法大致如下: (後面的例子都是基於antd,如何新增支援和按需載入,請看官網文件或者我上傳的程式碼配置,就不細說與hooks無關的東西了)
import React, {PureComponent} from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'
// class元件
class UserInfo extends PureComponent {
constructor(props) {
super(props);
// state初始化
this.state = {
title: ""
};
}
componentDidMount(){
// 解構state
const { title } = this.state;
this.doSearch(title);
}
doSearch = (title) => {
this.props.getUserInfo(title);
}
handleChange=(e) => {
// 更新state
this.setState({
title:e
})
}
render() {
const { list=[] } = this.props.userInfo;
return (
<div className={style.userInfo}>
<p>
標題:<Input style={{width:'100px'}} onChange={this.doSearch} />
<Button type="primary" onClick={this.doSearch}>查詢</Button>
</p>
<List
header={<div>列表</div>}
footer={null}
bordered
dataSource={list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
}
export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製程式碼
從上面程式碼中,我們看到了state三個相關的地方,初始化---更新---解構獲取,接下來我們使用hooks來寫。
import React, { useState, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'
// 函式式元件
const UserInfo = (props) => {
// 初始化state
const [title, setTitle] = useState("");
const [search, setSearch] = useState(false);
// 查詢資料
useEffect(() => {
props.getUserInfo(title);
}, [search]);
// 引數變化
const handleChange=(e) => {
setTitle(e.target.value);
}
// 執行查詢
const doSearch = () => {
setSearch(!search);
}
const { list=[] } = props.userInfo;
return (
<div className={style.userInfo}>
<p>
標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
<Button type="primary" onClick={() => { doSearch() }}>查詢</Button>
</p>
<List
header={<div>列表</div>}
footer={null}
bordered
dataSource={list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製程式碼
讓我們對比一下,都有哪些不同的地方
- 統一元件,所有元件都是無狀態元件(Funciont)
- 不再需要繫結this
- 沒有建構函式,內部狀態使用useState
- 沒有複雜的生命週期,生命週期使用useEffect(注意這裡的引數,它改變就會執行useEffect)
- 改變state使用它自身提供的setSearch方法
根據上面對比的結果,我們發現,以後寫元件只需要寫無狀態元件了。並且不需要煩惱this。生命週期也不需要寫那麼多,一個useEffect就夠了。並且你靈活控制它的第二個引數,可以決定什麼時候去執行它。是不是方便很多。
useReducer對比useState
官網對於內部狀態管理,提供了兩個方法,另外一個就是useReducer。
如果你熟悉redux的話,你會覺得它很眼熟。我們先寫一個,用userReducer來替代useState,然後在來說說為什麼用它!
import React, { useReducer, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import style from './index.less'
function reducer(state, action) {
switch (action.type) {
case 'change':
return {
...state,
...action.playod
};
}
}
const UserInfo = (props) => {
// 初始化reducer
const initialState = {title: "", search:false};
const [state, dispatch] = useReducer(reducer, initialState);
// 查詢資料
useEffect(() => {
props.getUserInfo(state.title);
}, [state.search]);
// 引數變化
const handleChange=(e) => {
dispatch({type: 'change', playod:{title:e.target.value}})
}
// 執行查詢
const doSearch = () => {
dispatch({type: 'change', playod:{search:!state.search}})
}
const { list=[] } = props.userInfo;
return (
<div className={style.userInfo}>
<p>
標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
<Button type="primary" onClick={() => { doSearch() }}>查詢</Button>
</p>
<List
header={<div>列表</div>}
footer={null}
bordered
dataSource={list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製程式碼
用法上和redux相似,通過純函式和action來執行改變。兩個對比之下差距不大,但是根據官方推薦和一些大佬的看法,推薦使用useReducer。
- 統一管理所有的狀態資料
- 可以處理比較複雜的資料結構
但是個人覺得和redux同時使用的話,對資料存放位置,和兩個dispatch的使用比較不友好。各位使用者根據自己的喜好選擇吧。
自定義hooks
除了官方提供的hooks,我們還可以自定義一些hooks,這裡我就根據我的需求定義一個請求資料的hooks。方便大家理解使用。
src/hooks/request.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const useSearchData = (url, params) => {
// 是否正在請求中
const [isLoading, setIsLoanding] = useState(false);
// 請求引數
const [queryParams, setQueryParams] = useState(params);
// 請求結果
const [data, setData] = useState([]);
// 查詢資料
const queryTableData = (params) => {
setIsLoanding(true);
axios.post(url).then((res)=>{
let data = JSON.parse(res.request.responseText);
const list = data.list.filter((item, index) => {
return item.indexOf(params) > -1
})
setIsLoanding(false);
setData(list);
})
}
// 根據引數變化決定是否請求資料
useEffect(() => {
queryTableData(queryParams);
}, [queryParams]);
// 供外部呼叫
const doRequest = (params) => {
setQueryParams(params);
}
return {
isLoading,
data,
doRequest
};
}
export default useSearchData;
複製程式碼
這裡我們寫了一個自定義hooks--useSearchData(注意,自定義的hooks要以use開頭,切記!!!)。它提供了我們需要的三個結果
- isLoading 是否在載入
- data 請求到的結果集
- doRequest 更換引數再一次請求
src/userInfo/index.js
import React, { useReducer, useEffect } from 'react';
import {connect} from 'react-redux';
import {getUserInfo} from "actions/userInfo";
import { Input, Button, List } from 'antd';
import useSearchData from 'hooks/request';
import style from './index.less';
function reducer(state, action) {
switch (action.type) {
case 'change':
return {
...state,
...action.playod
};
}
}
const UserInfo = (props) => {
// 初始化reducer
const initialState = {title: "", search:false};
const [state, dispatch] = useReducer(reducer, initialState);
const {isLoading, data, doRequest} = useSearchData('/api/getList',state.title);
// 查詢資料
useEffect(() => {
props.getUserInfo(data);
}, [data]);
// 引數變化
const handleChange=(e) => {
dispatch({type: 'change', playod:{title:e.target.value}})
}
// 執行查詢
const doSearch = () => {
doRequest(state.title);
}
const { list=[] } = props.userInfo;
return (
<div className={style.userInfo}>
<p>
標題:<Input style={{width:'100px'}} onChange={(e) => { handleChange(e) }} />
<Button type="primary" onClick={() => { doSearch() }}>查詢</Button>
</p>
<List
header={<div>列表</div>}
footer={null}
loading={isLoading}
bordered
dataSource={list}
renderItem={item => (<List.Item>{item}</List.Item>)}
/>
</div>
)
}
export default connect((userInfo) => userInfo, {getUserInfo})(UserInfo);
複製程式碼
在我們的UsesInfo元件中,我們首先呼叫useSearchData獲取isLoading, data, doRequest。在useEffect鉤子中根據data來判斷是否要更新資料到redux。最後當我們更改查詢引數需要更新list的時候,執行doRequest即可。
這樣我們的自定義查詢hooks就寫好了,其實就是把公共的東西抽取出來,封裝成一個hooks。尤其是以前的高階元件和渲染元件,層級巢狀過深,不好維護。寫成hooks的形式,都是函式式元件,無巢狀層級。
結尾和GIT地址
本人在專案中常用的hooks就介紹完了,有些人可能覺得加上了useContext是不是完全可以替代redux?個人理解還是不一樣,全域性的狀態管理,最好還是依賴於redux和mobx做管理較好。請各位小夥伴自行體驗!!! GIT地址