Hooks产生的背景
hooks式编程方法是居于FP (函数式编程)思想的实现。那么什么是函数式编程呢?具体有哪些特点呢。
FP与OOP的区别
FP(函数式编程)和OOP(面向对象式编程)两者之间存在共通点,实质上都是在描述构建组合的方法。本质上的区别是构建组合时,组合形式的不同。我们可以从两种思想的特点中看出端倪。
面向对象编程
面向对象的三大特性:封装、继承、多态。不难看出,前两点特性是在描述生成组合内元素、以及生成包含与被包含关系的组合。这两者在面向对象编程中是不可分割的,继承中包含着封装。而多态便是对一定范围内的组合中,元素与元素之间的差异化的描述。比如说父层组合元素中与子层组合元素中具有差异。更多时候。是在描述同一组合中,不同元素中存在差异。面向对象具有多态特征正是由封装和继承这两大特性决定的。
函数式编程
而函数式编程的最大特征:函数是第一等公民。意思就是说,这个是一种思维分片的编程方式。并没有给构建组合的形式赋予太多规则和限制,构建组合的自由度更高。
从上面的特征中,不难看出。函数式编程与面向对象编程的区别在于构建组合时自由度的不同。也就是说FP采用的是松耦合的方式,灵活性更高,OOP采用的是紧耦合的方式,严谨性更高。两种思想都是指导如何构建组合的。
面向对象式编程的组合图解
函数式编程的组合图解
有了这些理论依据,接下来,我们将讨论React中对函数式编程思想的一些具体实现,以及对函数式思想实践的演变。
洋葱圈模型与高阶组件
从上图中,我们可以看出,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,我们不难发现。这些应用实践都是为了把业务与视图解耦开来,能把视图和数据进行业务拆分,提高了可复用性,达到了业务分离的效果。这些实践更为贴切函数式编程达到逻辑分片的思想。