Javascript & React - 如何在不使用字典/对象的情况下避免“如果地狱”?

问题描述 投票:0回答:2

我知道这个问题以前曾被问过,但通常的答案给我带来了另一个(潜在的)问题和疑问。

背景

我有一个函数,它根据两个参数

paramA
paramB
返回必须渲染的组件。现在的代码看起来像这样:

  if (paramA === paramATypes.PRE) {
    if (paramB === paramBTypes.REQUEST) {
      detailedView = (
        <ComponentA
          requestDetails={requestDetails as RequestDetailsDto<ComponentADto>}
        />
      );
    } else if (paramB === paramBTypes.MODIFICATION) {
      detailedView = (
        <ComponentB
          requestDetails={
            requestDetails as RequestDetailsDto<ComponentBDto>
          }
        />
      );
    }
  } else if (paramA === paramATypes.PRI) {
    if (paramB === paramBTypes.REQUEST) {
      detailedView = (
        <ComponentC
          requestDetails={requestDetails as RequestDetailsDto<ComponentCDto>}
        />
      );
    } else if (paramB === paramBTypes.MODIFICATION) {
      detailedView = (
        <ComponentD
          requestDetails={
            requestDetails as RequestDetailsDto<ComponentDDto>
          }
        />
      );
    } 
  } else if...

这种情况一直持续下去,因为我们每种类型都有一些不同的类型,并且每种类型都有一个特定的组件,以某种方式呈现其属性。当然,这有点过于简单化了,只是为了能够描述情况。

问题是,我可以尝试做这样的事情,就像我通常使用更简单的值一样:

const dict = {
  [paramATypes.PRE]: {
    [paramBTypes.REQUEST]: <ComponentA
      requestDetails={requestDetails as RequestDetailsDto<ComponentADto>}
    />,
    [paramBTypes.MODIFICATION]: <ComponentB
      requestDetails={
        requestDetails as RequestDetailsDto<ComponentBDto>
      }
    />,
  }, 
  ...
}

然后就这样称呼它:

const view = dict[paramA][paramB];

问题

我看到的问题是,使用“if-hell”时,仅当满足 if 条件时才会处理组件的值。因此,在这种情况下,每次调用只会计算/处理一个组件。

但是,如果我使用对象/字典范例,它将处理所有值,因为它需要它来创建稍后要访问的实际对象,因此每次调用此方法都会计算所有可能性,然后返回其中之一。

如果这些值只是声明或更简单的值,我不会有任何问题,但作为 React 组件我不太确定。

问题

我对两种范式中处理它的方式是否正确?我该怎么做才能拥有更干净的条件?

也许将值定义包装在方法中,因此仅当我执行方法的结果时才会处理它,如

const view = dict[paramA][paramB]();

我只是想知道哪种方式是最好的表达方式,这样它不仅更容易阅读,而且还具有良好的性能(并且在代码分析器中具有良好的认知复杂性)。

谢谢!

javascript reactjs if-statement resourcedictionary cognitive-complexity
2个回答
2
投票

据我所知,有两种可能的解决方案:

  • 最好的,并不总是可行的 标准化要渲染的可能组件的所有道具并创建这样一个字典
const dict = {
  [paramATypes.PRE]: {
    [paramBTypes.REQUEST]: ComponentA,
    [paramBTypes.MODIFICATION]: ComponentB,
  },
} as const satisfies Record<
  paramATypes,
  Record<paramBTypes, React.FC<{ requestDetails: RequestDetailsDto }>>
>

然后在组件中执行如下操作:

const Component = () => {
  const View = dict[paramA][paramB]

  return (
    <div>
      <View requestDetails={requestDetails} />
    </div>
  )
}

}
  • 最灵活,但不太理想
const Component = () => {
  const dict = {
    [paramATypes.PRE]: {
      [paramBTypes.REQUEST]: () => (
        <ComponentA
          requestDetails={requestDetails as RequestDetailsDto<ComponentADto>}
        />
      ),
      [paramBTypes.MODIFICATION]: () => (
        <ComponentB
          requestDetails={requestDetails as RequestDetailsDto<ComponentBDto>}
        />
      ),
    },
  } as const satisfies Record<paramATypes, Record<paramBTypes, React.FC>>

  const View = dict[paramA][paramB]

  return (
    <div>
      <View />
    </div>
  )
}


在这两种情况下,仅渲染正确的组件,而不是所有可能的组件,因此从性能角度来看,您的表现很好

你应该尝试第一个选项,因为它使道具统一并使一切更易于维护,


0
投票

只是发人深省,因为它不太常用,但确实避免了 if-else 以及字典(其他解决方案仍然使用字典):您可以将

if-else
编码到数据结构中并制作它几乎看不见:

/* This is the data structure that "stands in" for if-conditionals */
function Conditional(component, predicate) {
  // A "conditional component" is an object with two fields:
  // - The component, tacked into a thunk
  // - A variadic predicate function
  const it = Object.create(Conditional.prototype);
  it.component = () => component;
  it.predicate = predicate;
  return it;
}

Conditional.getRenderer = (...conditionals) => (...params) => {
  // Takes N conditional components and returns a renderer function that awaits data
  return conditionals.reduce(
    (result, conditional) => result == null ? conditional.render(...params) : result,
    null
  );
};

Conditional.prototype.render = function (...params) {
  // Renders a conditional component if the associated predicate accepts the data
  return this.predicate(...params) ? this.component() : null;
};



/* These are the "dummy" components */
const ComponentA = (details) => `A(${details.msg})`;
const ComponentB = (details) => `B(${details.msg})`;
const ComponentC = (details) => `C(${details.msg})`;


/* This is the 'render' function that encodes the 'if-else' branching */
const render = Conditional.getRenderer(
  Conditional(ComponentA, (a, b) => a === 'a' && b < 2),
  Conditional(ComponentB, (a, b) => a === 'b'),
  Conditional(ComponentC, () => true)
);



/* Try it */
console.log(render('a', 0)({ msg: 'details' }));       // Should be 'A(details)'
console.log(render()({ msg: 'more details' }));        // Should be 'C(more details)'
console.log(render('b', 2)({ msg: 'other details' })); // Should be 'B(other details)'

请注意,涉及一种“后备”组件(ComponentC)
它有一个始终返回 true 的关联谓词。打电话时

getRenderer
该组件必须是最后输入的!

© www.soinside.com 2019 - 2024. All rights reserved.