Reactjs 如何重构处理过多条件/用例的react组件?
我希望将可靠的原则应用于处理过多“用例”的react组件。例如,假设我有一个组件,它的主要职责是呈现如下表:Reactjs 如何重构处理过多条件/用例的react组件?,reactjs,data-structures,refactoring,javascript-objects,solid-principles,Reactjs,Data Structures,Refactoring,Javascript Objects,Solid Principles,我希望将可靠的原则应用于处理过多“用例”的react组件。例如,假设我有一个组件,它的主要职责是呈现如下表: return ( <Table dataSource="" name={tableName} /> ) 这只是一个例子。想象一下,一些dataSource的name属性嵌套了3层。然后,其他dataSource甚至会有不同的键名称(尽管我需要渲染的结果数据实际上是相同的)。不仅如此,根据数据源,我可能需要调用不同的端点来执行一些功能(同样,函数也在
return (
<Table dataSource="" name={tableName} />
)
这只是一个例子。想象一下,一些dataSource
的name
属性嵌套了3层。然后,其他dataSource
甚至会有不同的键名称(尽管我需要渲染的结果数据实际上是相同的)。不仅如此,根据数据源
,我可能需要调用不同的端点来执行一些功能(同样,函数也在做相同的事情,只是端点可能不同)。因此,在同一组件中,我将具有如下功能:
const exportTable = () => {
if(dataSource === 'dataSourceA') {
// use endpoint A
} else if (dataSource=== 'dataSourceB') {
// use endpoint B
} else {
// use endpoint C
}
}
重构此类组件并使其更易于维护的最佳方法是什么?稍后,我们可以有10种类型的
数据源
,我不能在组件中使用if-else条件来满足它们的差异。在纯功能风格中,您可以接受功能以及道具的普通对象:
if(props.name instanceof函数)
props.name=props.name(数据源)//或其他一些参数
通过这种方式,您可以轻松地将细节转移给消费者,而不是将所有内容都放在一个地方
那么,对于端点,为什么逻辑应该驻留在表组件中?保持纯粹的视觉效果,让家长传递细节。在React中,我们将视觉和业务逻辑保存在单独的组件中,以便您可以轻松地在应用程序的其他位置重用视图。您可以使用挂钩来抽象数据的获取方式:
const{rows,…data}=useData(数据源)
返回(
);
在useData
hook中:
函数useData(数据源){
如果(数据源=='dataSourceA'){
//使用端点A
}else if(数据源==='dataSourceB'){
//使用端点B
}否则{
//使用端点C
}
// ...
返回数据;
}
您仍然需要处理这些条件,但它们将与UI分离,这将使管理组件生命周期变得更容易
其次,您可以创建一个服务/API层来抽象数据获取
异步函数fetchFromEndpointA(args){
const response=等待httpClient
.get(`/endpointA/${args}`)
返回响应.body;
}
API层将被钩子消耗:
//react async useAsync是帮助管理异步状态的库
从'react async'导入{useAsync};
函数useData(数据源){
const a=useAsync({promiseFn:fetchFromEndpointA,defer:true});
const b=useAncync({promiseFn:fetchFromEndpointB,defer:true});
如果(数据源=='dataSourceA'){
const{run,error,isLoading data}=a;
a、 run();
返回{错误,正在加载,数据};
}
// ...
}
您还可以从钩子中提取数据源解析+抓取。在不了解数据源对象的细节的情况下,我只能推荐一种通用策略。它可能看起来像:
异步函数获取数据(数据源,parseableObject){
如果(数据源=='dataSourceA'){
返回parseableObject['name'];
}else if(数据源==='dataSourceB'){
const name=parseableObject[0][0]。name;//无论name的路径是什么
const data=wait callEndpointB(名称);
返回数据.result;
}否则{
// ...
}
}
现在,任何钩子或组件都可以调用actaindata
,而无需知道条件。钩子/组件只需要跟踪异步状态
例如,在挂钩中:
函数useData(数据源,可解析对象){
返回useAsync({promiseFn:()=>getaindata(dataSource,parseableObject)});
}
或者,只需在组件中调用它并完全放弃自定义挂钩:
const{rows;
返回(
);
在做出决定之前,你可能需要探索很多可能性
最后,关于重构的一些常见建议:
- 在抽象或重构之前,首先通过可靠的、人类可读的测试来纠正所有具体的高级行为。快速和肮脏的代码是好的。不要依赖于测试实现细节或内部抽象。然后,如果以后出现了清晰的模式,您可以使用现有的测试进行重构来指导您
- 考虑一下成本。有一句程序员的谚语,大意是“没有抽象比有错误的抽象要好”。如果你决定将代码重新组织到不同的层中,你需要主动意识到它对可维护性的影响,等等。请查看。与其让我在这里总结,不如让你看/读它,并收集自己的见解
- 我喜欢。它很整洁,很好地抽象事物
或者,我已经完成了您在过去的
useffect
函数中描述的操作。
注意:
usemo
可能也有理由,但我将重点介绍useffect
从“../util/for/table/data”导入{parseTableData,TableData};
界面道具{
data:TableData;//键入这个将非常有用
名称:字符串;
}
常量表:React.FC=({name,data})=>{
const[tableData,setTableData]=useState();//在init上未定义
使用效果(
()=>parseTableData(setTableData,data),
[数据,可设置数据]
);
如果(!tableData){
返回解析…请稍候。
}
//在这里,您可以使用'tableData',因为它已正确格式化
返回。。。
}
然后,您可以将解析器逻辑抽象到单独的文件中:
接口表数据{
...
}
导出常量parseTableData=(
可设置数据:
const exportTable = () => {
if(dataSource === 'dataSourceA') {
// use endpoint A
} else if (dataSource=== 'dataSourceB') {
// use endpoint B
} else {
// use endpoint C
}
}