React v16.7.0-alpha中加入的新特性Hooks。它可让你在函数式组件中使用state和生命周期。本人花了点时间研究了下,发现网上大部分的教程都是偏向于理论,或者干脆翻译官网和代码,缺乏实际使用场景的教程,因此本人根据本身的理解写了这篇从开发角度去看这个hooks新特性(高手勿拍)react
理论方面的我不会讲太多,你们能够搜一搜,很是多的文章。或者直接去看官网。我主要会讲我在项目开发中会怎么使用它。ios
简单的来讲,就是让你的函数式无状态组件,支持使用状态组件的state和生命周期。另外能够解决this绑定这个麻烦的东西。(代码量也会减小不少)git
相对我开发中经常使用的hooks大体有useState、useReducer、useEffect。另外还有一些我自定义的hooks。github
接下来咱们直接开始用,跳过使人头疼的理论,直接在使用中理解他们究竟是干什么的。redux
首先咱们须要一个基于react的开发框架,这里打个小广告,推荐本身前段时间分享的一篇文章《如何搭建一个REACT全家桶框架》。axios
在原先的userInfo页面里面进行一个常规查询列表功能的编写。不用hooks功能的写法大体以下: (后面的例子都是基于antd,如何添加支持和按需加载,请看官网文档或者我上传的代码配置,就不细说与hooks无关的东西了)api
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来写。bash
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);
复制代码
让咱们对比一下,都有哪些不一样的地方antd
根据上面对比的结果,咱们发现,之后写组件只须要写无状态组件了。而且不须要烦恼this。生命周期也不须要写那么多,一个useEffect就够了。而且你灵活控制它的第二个参数,能够决定何时去执行它。是否是方便不少。数据结构
官网对于内部状态管理,提供了两个方法,另一个就是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。方便你们理解使用。
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开头,切记!!!)。它提供了咱们须要的三个结果
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的形式,都是函数式组件,无嵌套层级。
本人在项目中经常使用的hooks就介绍完了,有些人可能以为加上了useContext是否是彻底能够替代redux?我的理解仍是不同,全局的状态管理,最好仍是依赖于redux和mobx作管理较好。请各位小伙伴自行体验!!! GIT地址