使用hooks重构antd pro的想象力(三)我是如何利用hooks干掉redux的

简介: 当然没有。我们将思维维度扩大至整个项目。项目中的大多数页面,首次加载时,都会去请求一个接口。这个操作是一个几乎一样的逻辑片段。因此我们可以利用自定义hooks的思维,将这个逻辑片段抽离出来,封装成为一个自定义hooks useInitial。首先,我们思考一下,这样一个页面首次加载需要请求数据的公共逻辑片段,需要维护什么状态?

使用hooks重构antd pro的想象力(三)我是如何利用hooks干掉redux的


微信图片_20220510231439.jpg


经过组件化思维的层层分析,我们将antd pro官方demo的分析页伪代码重构至如下:


export function Analysis() {
  const [dashboardAnalysis, setDashboardAnalysis] = useState<AnalysisData>();
  const [loading, setLoading] = useState(true);
  const {visitData, salesData, searchData, salesPieData, offlineData} = dashboardAnalysis;
  useEffect(() => {
    if (!loading) {
      return;
    }
    api().then(res => {
      setDashboardAnalysis(res.data);
    })
  }, [loading]);
  return (
    <GridContent>
      // 四个长相类似的块合并成一个组件处理
      <IntroduceRow visitData={visitData} />
      // 销售额访问量的图表
      <SalesCard salesData={salesData} selectDate={() => setLoading(true)} />
      // 线上热门搜索
      <TopSearch searchData={searchData} />
      // 销售额类别占比
      <ProportionSales salesPieData={salesPieData} />
      // 离线数据图表
      <OfflineData offlineData={offlineData} />
    </GridContent>
  );
}


那么到这里完了吗?


当然没有。我们将思维维度扩大至整个项目。


项目中的大多数页面,首次加载时,都会去请求一个接口。这个操作是一个几乎一样的逻辑片段。因此我们可以利用自定义hooks的思维,将这个逻辑片段抽离出来,封装成为一个自定义hooks useInitial


首先,我们思考一下,这样一个页面首次加载需要请求数据的公共逻辑片段,需要维护什么状态?


第一个:请求的数据结果 设定泛型参数


第二个:表示正在请求的状态 loading


第三个:出现异常时的提示语句


第四个:传入的参数有哪些,如果参数更改,还得重新请求接口


其他的根据实际情况的不同,还会需要新增更多的参数,这里暂时就这四个。


其次,我们思考第二个问题,自定义hooks需要接收哪些参数?


第一个:api请求函数


第二个:api请求函数的参数


第三个:数据的默认值


也就是说,我们要把该接口涉及到的所有逻辑都放在该自定义hooks中统一处理。把不同的元素都作为参数传入即可。


封装结果如下:


import { useState, useEffect } from 'react';
export type APIFunc<T, P> = (params: P) => Promise<T>
/**
 * @param {api} —api.ts件中封装的接口请求方法
 * @param {defaultData} 页面初始化时接口数据的默认值
 * @param {params} 接口所需要的参数  注意,这里请传入接口需要的完整的参数
 * @param {delay} 当该值为true时,接口不请求
 */
export default function useInitial<T, P>(
  api: APIFunc<T, P>, 
  defaultData: T, 
  _params: P, 
  delay?: boolean
) {
  const [store, setStore] = useState({
    params: _params,
    loading: true,
    data: defaultData,
    errMsg: ''
  });
  const {params, loading, data, errMsg} = store;
  useEffect(() => {
    if (!loading || delay) { return; }
    getData(params);
  }, [loading]);
  function getData(params: P) {
    api(params).then(res => {
      // 这里需要注意,要结合自己团队定义的接口规范返回结果
      let data = res || defaultData;
      setStore({
        ...store,
        errMsg: '',
        loading: false,
        data
      })
    }).catch(e => {
      setStore({
        ...store,
        errMsg: e.message,
        loading: false
      })
    });
  }
  function setParams(params: P, refreshing: boolean) {
    setStore({...store, params});
    refreshing && getData(params);
  }
  return {
    loading,
    setLoading: (loading: boolean) => setStore({...store, loading}),
    data,
    errMsg,
    setParams,
  };
}


这样封装之后,使用起来就非常简单。


使用时,只需要一句代码,我们就能够获取到我们想要的数据,状态,对应的操作方法等。


const {
    loading, data, setParams, errMsg, setLoading
  } = useInitial<AnalysisData, null>(fakeChartData, initState, null)


因此,页面组件里的逻辑就变得非常简单,完整可运行的代码如下:


import React from "react";
import { Col, Row } from 'antd';
import { GridContent } from '@ant-design/pro-layout';
import { fakeChartData } from "./service";
import { AnalysisData } from './data.d';
import useInitial from '@/hooks/useInitial';
import { initState } from '@/pages/dashboard/analysis/model';
import IntroduceRow from "./components/IntroduceRow";
import SalesCard from "./components/SalesCard";
import TopSearch from "./components/TopSearch";
import ProportionSales from "./components/ProportionSales";
import OfflineData from "./components/OfflineData";
export default function AnalysisFC() {
  const {
    loading, data, setParams, errMsg, setLoading
  } = useInitial<AnalysisData, null>(fakeChartData, initState, null);
  const {
    visitData, visitData2, salesData, searchData, offlineData, offlineChartData,
    salesTypeData, salesTypeDataOnline, salesTypeDataOffline,
  } = data;
  const salesPieData = {
    all: salesTypeData,
    online: salesTypeDataOnline,
    stores: salesTypeDataOffline
  };
  if (errMsg) {
    // 处理异常逻辑
  }
  return (
    <GridContent>
      <IntroduceRow visitData={visitData} loading={loading} />
      {/* 这里应该调用setParams(xxxx, true),传入参数并且根据新参数重新请求接口,但是由于是demo接口设计不够合理,没有参数,因此直接调用setLoading刷新接口即可, */}
      <SalesCard loading={loading} salesData={salesData} onChange={() => setLoading(true)} />
      <Row gutter={24} type="flex" style={{ marginTop: 24 }}>
        <Col xl={12} lg={24} md={24} sm={24} xs={24}>
          <TopSearch loading={loading} visitData2={visitData2} searchData={searchData} />
        </Col>
        <Col xl={12} lg={24} md={24} sm={24} xs={24}>
          <ProportionSales loading={loading} salesPieData={salesPieData} />
        </Col>
      </Row>
      <OfflineData loading={loading} offlineData={offlineData} offlineChartData={offlineChartData} />
    </GridContent>
  );
}


我们发现,优化之后的代码,在页面组件里,几乎没有冗余的额外逻辑影响阅读。简单干净利落!


整个优化思维基本上就是这样。通过引入hooks,并借助巧妙的组件化思维,我们将复杂的页面一层层变得非常简单。


最后思考一个问题:


先看图:


微信图片_20220510231442.jpg


我们仔细思考Ant Design Pro项目的整个结构。哪些元素是属于共有的?


头部Header模块。左侧导航模块。设置模块。


当页面切换时,我们发现这些模块始终存在。而变化的,仅仅只是中间的页面模块。

那么,我们是不是可以把这些固定的模块统一整合在同一个顶层页面组件App里?


当然可以


export default function App() {
  return (
    // 头部组件
    <Header />
    // 左侧导航
    <NavSide />
    // 设置
    <Setting />
    // 变化的页面组件
    <Pages />
  );
}


而我们还发现,这些组件的数据,其实是相互不干扰的。也就是说,从这个角度来思考,整个项目里,已经没有真正意义上的共享状态了。


那么意味着什么?


意味着,在这样的组织架构下,我们完全可以不再使用dva中的那一套数据逻辑,redux可以不用了,redux-saga可以不用了,甚至useDispatch可以不用了,useSelecotr也可以不用了,仅仅只使用最简单的hooks的方式来维护数据就足够了。


按照这个思路,大家可以动手改造试试看。改造好了,整个项目就重构完了。

相关文章
|
存储 前端开发 JavaScript
ahooks 正式发布:值得拥抱的 React Hooks 工具库
ahook定位于一套基于 React Hooks 的工具库,核心围绕 React Hooks 的逻辑封装能力,降低代码复杂度和避免团队的重复建设为背景,共同建设和维护阿里经济体层面的 React Hooks 库。
22385 1
ahooks 正式发布:值得拥抱的 React Hooks 工具库
|
1月前
|
前端开发
React Hooks实战技巧:提升你的组件开发效率
【7月更文挑战第16天】React Hooks为React开发带来了革命性的变化,使得函数式组件更加强大和灵活。通过掌握上述实战技巧,你可以更高效地编写清晰、可维护和可复用的React组件。希望这些技巧能帮助你在React开发之路上走得更远。
|
2月前
|
缓存 前端开发 JavaScript
React Hooks 一步到位
React Hooks 一步到位
|
3月前
|
前端开发
React Hooks - useState 的使用方法和注意事项(1),web前端开发前景
React Hooks - useState 的使用方法和注意事项(1),web前端开发前景
|
3月前
|
前端开发
【边做边学】系统解读一下React Hooks
【边做边学】系统解读一下React Hooks
|
3月前
|
缓存 前端开发 API
探索 React Hooks 的世界:如何构建出色的组件(上)
探索 React Hooks 的世界:如何构建出色的组件(上)
探索 React Hooks 的世界:如何构建出色的组件(上)
|
3月前
|
存储 前端开发 JavaScript
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)
|
10月前
|
设计模式 前端开发
【整活】👉被玩坏的React hook组件通信👈
【整活】👉被玩坏的React hook组件通信👈
|
存储 前端开发 JavaScript
React useReducer 终极使用教程
useReducer 是在 react V 16.8 推出的钩子函数,从用法层面来说是可以代替useState。相信前期使用过 React 的前端同学,大都会经历从 class 语法向 hooks 用法的转变,react 的 hooks 编程给我们带来了丝滑的函数式编程体验,同时很多前端著名的文章也讲述了 hooks 带来的前端心智的转变,这里就不再着重强调,本文则是聚焦于 useReducer 这个钩子函数的原理和用法,笔者带领大家再一次深入认识 useReducer。
React useReducer 终极使用教程
|
前端开发
React Hooks在平时开发中需要注意的事项?
React Hooks在平时开发中需要注意的事项?
103 0