React中自定义Hooks的实际意义与应用

520 阅读3分钟

Hooks产生的背景

hooks式编程方法是居于FP (函数式编程)思想的实现。那么什么是函数式编程呢?具体有哪些特点呢。

FP与OOP的区别

FP(函数式编程)和OOP(面向对象式编程)两者之间存在共通点,实质上都是在描述构建组合的方法。本质上的区别是构建组合时,组合形式的不同。我们可以从两种思想的特点中看出端倪。

面向对象编程

面向对象的三大特性:封装、继承、多态。不难看出,前两点特性是在描述生成组合内元素、以及生成包含与被包含关系的组合。这两者在面向对象编程中是不可分割的,继承中包含着封装。而多态便是对一定范围内的组合中,元素与元素之间的差异化的描述。比如说父层组合元素中与子层组合元素中具有差异。更多时候。是在描述同一组合中,不同元素中存在差异。面向对象具有多态特征正是由封装和继承这两大特性决定的

函数式编程

而函数式编程的最大特征:函数是第一等公民。意思就是说,这个是一种思维分片的编程方式。并没有给构建组合的形式赋予太多规则和限制,构建组合的自由度更高。

从上面的特征中,不难看出。函数式编程与面向对象编程的区别在于构建组合时自由度的不同。也就是说FP采用的是松耦合的方式,灵活性更高,OOP采用的是紧耦合的方式,严谨性更高。两种思想都是指导如何构建组合的。

面向对象式编程的组合图解

u=1460176055,838303812&fm=253&fmt=auto&app=120&f=JPEG.webp

函数式编程的组合图解 src=http___img.136.la_20210409_1358fd660c0c4740bd48915530b37e90.jpg&refer=http___img.136.webp

有了这些理论依据,接下来,我们将讨论React中对函数式编程思想的一些具体实现,以及对函数式思想实践的演变。

洋葱圈模型与高阶组件

circle.webp

从上图中,我们可以看出,FP通过洋葱圈模型的方式可以做到OOP的效果。React中的高阶组件便是其中一个例子。高阶组件采用的是洋葱圈模型,正是体现了面向对象的思想。在实际应用中,我们需要对组件赋能的时候需要用到高阶组件。

  //公共的业务组件
  const Child = props => {
    return <div>{props?.child}</div>;
  };
  
  //公共的业务逻辑
  const servicesApi = () => {
      //do something
      //service
      const data = {};
      return data;
  }
  
  //最后达到的效果便是,剥离公共业务与公共UI的展示
  const WithHOC = (Wrapper,services) => {
    const state = services;
    return <Wrapper {...state} />
  };
  
  WithHOC(Child)

最为常见的高阶组件便是剥离统一业务的组件,达到业务与UI展示逻辑分离的作用。

当然还有很多使用高阶组件的场景,比如做权限校验、逻辑拦截这些给基础组件赋能的场景等等。

更为灵活的自定义Hooks

用自定义hooks的方式,在函数式组件的运用,剥离业务逻辑和视图的做法,更加体现了函数式编程思想的优点。

  import requestCountryList from '@/common/api'
  
  // 把主要的获取数据的逻辑封装到每个特定的自定义hooks中
  //业务逻辑
  const useRequestCountryList = () => {
     const [data,setData] = useState();
     
     const getCountryList = () => {
        const res = await requestCountryList();
        if(res.data){
          setData(res.data)
        }
     }
  
     useEffect(() => {
         //get some dataSource
         getCountryList()
     },[])
     
     return data;
  }
  
  //负责渲染数据的视图
  const ListItem = props => <div>
     {props.name}
  </div>
  
  //实现业务数据与视图分离
  const CountryList = () => {
      //获取数据源
      const list = useRequestCountryList();
      //依靠数据渲染组件
      return list.map(item => <ListItem {...item} /> )
  }

对比高阶组件与自定义hooks,我们不难发现。这些应用实践都是为了把业务与视图解耦开来,能把视图和数据进行业务拆分,提高了可复用性,达到了业务分离的效果。这些实践更为贴切函数式编程达到逻辑分片的思想