react中的组件设计
react的组件设计有很多模式。下面列举几种常见的:
完全受控组件
这种组件设计的特点是,组件的所有state和action都来自props,组件自身没有状态,只负责展示UI层。model层完全交给全局状态管理库比如redux或mobx。不推荐这种组件设计,因为后期不好维护。这是典型的滥用全局状态管理库的现象。
什么叫滥用全局状态管理库?
就是没有认识到状态管理库的作用,或者说我们什么时候需要状态管理库?
拿 react 来说,react 是有组内状态的,状态可以通过 props 传递。但是,但当 app 比较庞大的时候,兄弟组件,远亲组件这些的交流就变得困难起来,
它们必须依赖相同的父组件来完成信息的传递。这时,就是我们使用状态管理库的时候。
但是,很多人把所有状态都往 redux 里面丢,虽然这方便了开发,但缺点却很明显:
- 组件很难复用:因为状态只有一份。
- 耦合度高:根据高内聚低耦合的设计原则,一个模块应该有独立的功能,不依赖外部,在内部实现复杂度,只暴露接口来与外界交流。但如果把组内的一些状态放在全局 model,就提供了让其他组件修改的能力,并且代码没有内聚。
非受控组件
划分好状态的等级,尽量把状态放在组件内。当遇到共享组内状态困难的场景时,提升状态到全局状态管理库。
这种组件,有view层、model层、services层。因为它是有独立功能的,然后通过向外界暴露api来提供自己的能力,同时把复杂度隐藏在内部。
例如一个列表组件:
// 方案一
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';
export default function ListDemo({requestId}){
// model
const [data,setData] = useState([]);
const [visible,setVisible] = useState(false);
useEffect(()=>{
// services 层
getData().then(data=>{
setData(data)
});
/**
* 当requestId变化时,列表会重新请求
* 这里的requestId是组件向外界暴露的一个api
**/
},[requestId])
useEffect(()=>{
if(visible===true){
// clearState
setVisible(false);
}
},[requestId])
return (
// view
<div>
{
data.map(item=><li>{item}</li>
}
{
visible && (
<div>
this is a modal
</div>
)
}
</div>
)
)
}
// app.jsx
<ListDemo />
这种组件设计的特点是,组件可以重置自身状态的时机是由自身控制的。如果你觉得这样麻烦,你可以把重置自身状态的时机交给外部,通过key来 “销毁组件”=>“重新渲染组件”。上面的代码可以简化成:
// 方案二
// ListDemo.jsx
import React,{useEffect} from 'react';
import {getData} from 'services/api';
export default function ListDemo({requestId}){
// model
const [data,setData] = useState([]);
const [visible,setVisible] = useState(false);
useEffect(()=>{
// services 层
getData().then(data=>{
setData(data)
});
},[])
return (
// view
<div>
{
data.map(item=><li>{item}</li>
}
{
visible && (
<div>
this is a modal
</div>
)
}
</div>
)
)
}
// app.jsx
/*
*当requestId变化时,ListDemo会重新渲染
*/
<ListDemo key={requestId} />
方案二的代码比较整洁,且出错率比方案一低,但是方案二存在重新渲染组件的一个环节,性能开支会比方案一多一点点(大部分情况你都可以忽略不计)。
很多情况下,我们应该采用方案二。
原文:https://zhuanlan.zhihu.com/p/88593781