记录一次完整的react hooks实践
写在前面
react在16.8版本正式发布了hooks。关注了很久,最近正好有一个小需求,赶紧来试一下。
需求描述
需求很简单,部门内部的一个数据查询小工具。大致长成下面这样:
用户首次访问页面,会拉取数据展示。输入筛选条件,点击查询后,会再次拉取数据在前端展示。
需求实现
使用react class component的写法
如果使用以前的class写法,简单写一下,代码可能大概长成下面这样:
import react from 'react'; import { tabs, input, rangetime, button, table } from './components'; class app extends react.component { ... state = { type: [], id: '', title: '', date: [], datalist: [] } componentdidmount() { this.fetchdata(); } render() { <tabs value={this.state.type} onchange={this.handletypechange}/> <input value={this.state.id} label="id" onchange={this.handleidchange}/> <input value={this.state.id} label="标题" onchange={this.handletitlechange}/> <rangetime value={this.state.date} onchange={this.handlerangetimechange}/> <button onclick={this.handlequerybtnclick}>查询</button> <table datalist={this.state.datalist} /> } fetchdata() { ... this.setstate({ datalist }); } handletypechange() { ... this.setstate({ type, }); } handleidchange() { ... this.setstate({ id, }); } handletitlechange() { ... this.setstate({ title, }); } handlerangetimechange() { ... this.setstate({ date, }); } handlequerybtnclick() { ... } ... }
使用react hooks的写法
关于react hooks的相关内容,这里就不赘述了。可以直接查看react官方文档,写得非常好。
本次需求其实就两个逻辑:1、输入筛选项 。2、查询数据
主页面一个hooks,处理筛选项以及数据展示。数据请求逻辑单独弄一个hooks。
主页面hooks:
import react, { usestate, useeffect} from 'react'; import { tabs, input, rangetime, button, table } from './components'; const app = () => { // 数据类型 const tabs = [{ key: 1, value: '类型1' }, { key: 0, value: '类型2' }]; const [tab, settab] = usestate(1); // 数据id const [dataid, setdataid] = usestate(''); // 标题 const [title, settitle] = usestate(''); // 时间区间, 默认为至今一周时间 const now = date.now(); const [timerange, settimerange] = usestate([now - 1000 * 60 * 60 * 24 * 7, now]); // 数据列表 const [datalist, setdatalist] = usestate([]); // 点击搜索按钮 function handlebtnclick() { // 请求数据 ... } return <section classname="app"> <title title="数据查询" /> <tabs label="类型" tabs={tabs} tab={tab} onchange={settab} /> <input value={dataid} placeholder="请输入数据id" onchange={setdataid}>id</input> <input value={title} placeholder="请输入数据标题" onchange={settitle}>标题</input> <timerange label="数据时间" value={timerange} onchange={handletimechange}/> <article classname="btn-container"> <button type="primary" onclick={handlebtnclick}> 查询 </button> </article> <table datalist={datalist}></table> </section> };
上面的代码,完成了筛选项的处理逻辑。下面来实现负责数据请求的hooks.
数据请求hooks:
import react, { usestate, useeffect } from 'react'; import jsonp from '../tools/jsonp'; function myfecth(url) { // 是否正在请求中 const [isloading, setisloanding] = usestate(false); // 请求参数 const [queryparams, setqueryparams] = usestate(null); // 请求结果 const [data, setdata] = usestate(null); // 向接口发起请求 const fetchdata = async () => { if(queryparams === null) { return; } setisloanding(true); const res = await jsonp({ url: url, data: queryparams }); setdata(res); setisloanding(false); } // 只要queryparams改变,就发起请求 useeffect(()=> { fetchdata(); }, [queryparams]); // 供外部调用 const doget = (params) => { setqueryparams(params); } return { isloading, data, doget } } export default myfecth;
在主页面中,引用数据请求hooks:
import react, { usestate, useeffect} from 'react'; import { tabs, input, rangetime, button, table } from './components'; import myfecth from './myfetch'; const app = () => { // ①使用数据请求hooks const { isloading, data, doget } = myfecth('http://xxx'); // 数据类型 const tabs = [{ key: 1, value: '类型1' }, { key: 0, value: '类型2' }]; const [tab, settab] = usestate(1); // 数据id const [dataid, setdataid] = usestate(''); // 标题 const [title, settitle] = usestate(''); // 时间区间, 默认为至今一周时间 const now = date.now(); const [timerange, settimerange] = usestate([now - 1000 * 60 * 60 * 24 * 7, now]); // 数据列表 const [datalist, setdatalist] = usestate([]); // 点击搜索按钮 function handlebtnclick() { // ②点击按钮后请求数据 const params = {}; title && (params.title = title); dataid && (params.dataid = dataid); params.starttime = string(timerange[0]); params.endtime = string(timerange[1]); doget(params); } // ③data改变后,重新渲染列表。 // 这里相当于 componentdidupdate。当data发生改变时,重新渲染页面 useeffect(() => { setdatalist(data); }, [data]); // ④首次进入页面时,无任何筛选项。拉取数据,渲染页面。 // useeffect第二个参数为一个空数组,相当于在 componentdidmount 时执行该「副作用」 useeffect(() => { doget({}); }, []); return <section classname="app"> <title title="数据查询" /> <tabs label="类型" tabs={tabs} tab={tab} onchange={settab} /> <input value={dataid} placeholder="请输入数据id" onchange={setdataid}>id</input> <input value={title} placeholder="请输入数据标题" onchange={settitle}>标题</input> <timerange label="数据时间" value={timerange} onchange={handletimechange}/> <article classname="btn-container"> <button type="primary" isloading={isloading} onclick={handlebtnclick}> 查询 </button> </article> <table datalist={datalist}></table> </section> };
关于react hooks的一些思考
使用hooks写完这个需求,最直观的感受就是,代码写起来很爽。不需要像以前那样写很多的setstate。其次就是
hooks的api设计得很优秀,一个usestate的就能将【状态】和【变更状态的逻辑】两两配对。react的基本思想就是【数据到视图的映射】,在hooks中,使用useeffect来表明其中的【副作用】,感觉react官方也倾向于不区分componentdidmount和componentdidupdate。
从api设计就能看出,hooks提倡组件状态细粒度地拆分。在一个hooks组件中,可能包含很多的状态,如果用户的某些操作,需要同时修改两个状态,那么我需要分别调用这两个状态的修改逻辑,这样会导致组件被重新render两次。而在使用class写法的组件中,只需要一次setstate就好。这样看来,hooks中render两次的操作,可能会带来些许的性能问题 ? 这就要求我们在设计组件结构和state时,多斟酌,多抽象。
关于hooks的一些faq,官方也有很棒的文档:
写在后面
本文通过工作中的一个小需求,完成了一次react hooks的实践,不过上述代码依然有很多需要优化的地方。这次实践让我最直观的接触了react hooks,也帮助自己进一步理解了react团队的一些思想。符合预期。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: react同构实践之实现自己的同构模板