如何在React组件中使用switch语句?

typ*_*pos 49 reactjs

我有一个React组件,render在组件的方法中我有这样的东西:

render() {
    return (
        <div>
            <div>
                // removed for brevity
            </div>

           { switch(...) {} }

            <div>
                // removed for brevity
            </div>
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

现在重点是我有两个div元素,一个在顶部,一个在底部,是固定的.在中间我想要一个switch语句,根据我的状态中的值,我想渲染一个不同的组件.所以基本上,我希望div始终修复这两个元素,并且只是在中间每次渲染一个不同的组件.我正在使用它来实现多步付款程序).虽然,代码目前它不起作用,因为它给我一个错误说这switch是意外的.任何想法如何实现我想要的?

Kel*_*oya 87

尝试这个,这也更清晰:在函数中从渲染中取出该开关,然后调用它传递你想要的参数.例如:

renderSwitch(param) {
  switch(param) {
    case 'foo':
      return 'bar';
    default:
      return 'foo';
  }
}

render() {
  return (
    <div>
      <div>
          // removed for brevity
      </div>
      {this.renderSwitch(param)}
      <div>
          // removed for brevity
      </div>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 如果将函数调用放在返回中,它总是在渲染时调用。因此,如果您需要从 return 语句以外的其他地方调用 renderSwitch ,那么这是行不通的。 (2认同)

1ve*_*ven 34

这是发生的,因为switch语句是一个statement,但这里javascript需要一个表达式.

虽然,不建议在render方法中使用switch语句,但可以使用自调用函数来实现:

render() {
    // Don't forget to return a value in a switch statement
    return (
        <div>
            {(() => {
                switch(...) {}
            })()}
        </div>
    );
}
Run Code Online (Sandbox Code Playgroud)

  • 为什么不推荐呢? (3认同)
  • 可能他在某个地方读到过一些东西,说不要这样做,因为这对某人来说是丑陋的。很多人出于不同的原因不喜欢 switch 语句。虽然 React 文档中没有提及,但 React 显然支持条件渲染,并且 switch 语句不会导致 React 出现任何问题。 (3认同)

Tom*_*cer 34

尽管这是另一种方法,但如果您全力以赴,则可以利用它useCallback来生成一个仅在必要时重新创建的函数。

假设您有一个应该根据statusprop 进行渲染的组件。使用钩子,您可以按如下方式实现:

const MyComponent = ({ status }) => {
  const renderContent = React.useCallback(() => {
    switch(status) {
      case 'CONNECTING': 
        return <p className="connecting">Connecting...</p>;
      
      case 'CONNECTED': 
        return <p className="success">Connected Successfully!</p>

      default: 
        return null;
      
    }
  }, [status]);

  return (
    <div className="container">
      {renderContent()}
    </div>
  );
};
Run Code Online (Sandbox Code Playgroud)

我喜欢这个是因为:

  • 很明显发生了什么 - 创建一个函数,然后调用它(立即调用的匿名函数方法看起来有点奇怪,并且可能会让新开发人员感到困惑)
  • useCallback钩子确保renderContent回调在渲染之间重用,除非依赖关系发生status变化
  • renderContent函数使用闭包来访问传递给组件的必要属性。一个单独的函数(如接受的答案)需要将 props 传递给它,这可能很麻烦(特别是在使用 TypeScript 时,因为参数也应该正确输入)


len*_*kan 27

与其他答案相比,我更倾向于在渲染函数中内联"切换".它使得在该位置可以呈现哪些组件更加清晰.您可以使用普通的旧JavaScript对象实现类似开关的表达式:

render () {
  return (
    <div>
      <div>
        {/* removed for brevity */}
      </div>
      {
        {
          'foo': <Foo />,
          'bar': <Bar />
        }[param]
      }
      <div>
        {/* removed for brevity */}
      </div>
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

  • 这种方法有其局限性和开销。您的每个视图都将被处理,并且取决于可能不存在的当前状态/道具。例如:假设您想要呈现:`&lt;SearchResults /&gt;` 或`&lt;NoResults /&gt;`。如果视图状态应该呈现 `&lt;NoResults /&gt;`,`&lt;SearchResults /&gt;` 可能无法编译,因为它依赖于尚不存在的属性。 (23认同)
  • @lama12345 对于默认情况,请使用`||`,如下所示:`{ 'foo': &lt;Foo /&gt;, 'bar': &lt;Bar /&gt; }[param] || &lt;巴兹/&gt;` (15认同)
  • 默认情况下怎么办? (11认同)
  • 这看起来棒极了!知道如何使用枚举作为该对象的键吗?即: `MyEnum.Foo: &lt;Foo /&gt;, MyEnum.Bar: &lt;Bar /&gt;` 我的编译器抱怨 `MyEnum` 和 `Foo` 之间有一个意外的标记。 (3认同)
  • @OfekGila 与定义任何奇怪的对象键相同,例如 [MyEnum.Foo]: &lt;Foo /&gt; (3认同)
  • 这很酷。我稍微修改了它以使用数组而不是 POJO,我只是使用索引将其括在数组中。 (2认同)
  • 这是在 Python 中处理 switch-case 的标准方法。在这种情况下,我更喜欢它,因为它具有出色的可读性。 (2认同)
  • 令人难以置信的是,人们提出了许多极其复杂的解决方案,而不是简单地这样做 (2认同)

Mat*_*Way 24

我不是任何当前答案的忠实粉丝,因为它们要么太冗长,要么需要您跳过代码以了解发生了什么。

我更喜欢通过创建一个<Switch/>. 这个组件的工作是获取一个道具,并且只渲染子道具与这个道具匹配的孩子。因此,在下面的示例中,我test在开关上创建了一个道具,并将其与子项上的value道具进行了比较,只渲染匹配的道具。

例子:

const Switch = props => {
  const { test, children } = props
  // filter out only children with a matching prop
  return children.find(child => {
    return child.props.value === test
  })      
}

const Sample = props => {
  const someTest = true
  return (
    <Switch test={someTest}>
      <div value={false}>Will display if someTest is false</div>
      <div value={true}>Will display if someTest is true</div>
    </Switch>
  )
}

ReactDOM.render(
  <Sample/>,
  document.getElementById("react")
);
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Run Code Online (Sandbox Code Playgroud)

您可以根据需要使切换变得简单或复杂。不要忘记对孩子和他们的价值道具进行更健壮的检查。

  • 正是我一直在寻找的东西!这里有一个小小的改进: ```javascript const Switch = props =&gt; { const { test, kids } = props; return child.find(child =&gt; { return child.props.value === test; }); }; const Case = ({ 子项, 值 }) =&gt; { 返回子项; // 我不想在我的箱子周围添加容器!}; 这样你就可以这样写:````javascript &lt;Switch test={true}&gt; &lt;Case value={true}&gt; &lt;ItAlwaysFeelsRightToBeTrue /&gt; &lt;/Case&gt; &lt;Case value={false}&gt; &lt;FalseAlarm /&gt; &lt; /案例&gt; &lt;/开关&gt; ``` (6认同)
  • 其 TypeScript 版本位于 /sf/answers/4469657321/。 (4认同)

Luk*_*rns 17

lenkan 的回答是一个很好的解决方案。

<div>
  {{ beep: <div>Beep</div>,
     boop: <div>Boop</div>
  }[greeting]}
</div>
Run Code Online (Sandbox Code Playgroud)

如果你需要一个默认值,那么你甚至可以做

<div>
  {{ beep: <div>Beep</div>,
     boop: <div>Boop</div>
  }[greeting] || <div>Hello world</div>}
</div>
Run Code Online (Sandbox Code Playgroud)

或者,如果这对您来说不太好,那么您可以执行以下操作

<div>
  { 
    rswitch(greeting, {
      beep: <div>Beep</div>,
      boop: <div>Boop</div>,
      default: <div>Hello world</div>
    }) 
  }
</div>
Run Code Online (Sandbox Code Playgroud)

function rswitch (param, cases) {
  if (cases[param]) {
    return cases[param]
  } else {
    return cases.default
  }
}
Run Code Online (Sandbox Code Playgroud)

  • {{key1: &lt;Component1 /&gt;, ...}[key] 不是一个好的解决方案。你看,在选择发生之前,整个初始对象被构造 - 即切换的每个分支都被渲染 - Component1、Component2 等...... (4认同)

arv*_*tal 9

一种使用条件运算符表示渲染块中的​​一种切换情​​况的方法:

{this.props.someVar == 1 &&
    <SomeContent/>
|| this.props.someVar == 2 &&
    <SomeOtherContent />
|| this.props.someOtherVar == 1 &&
    <YetSomeOtherContent />
||
    <SomeDefaultContent />
}
Run Code Online (Sandbox Code Playgroud)

我建议添加一些括号以确保预期结果,除非掌握运算符优先级...

  • 漂亮又优雅,可以直接在渲染块中使用。最佳答案 恕我直言 (2认同)
  • 我注意到这违反了 EsLints 规则 https://eslint.org/docs/rules/no-mixed-operators 混合 &amp;&amp; 和 || (2认同)
  • @FishFingers当我尝试完全按照上面的方式使用它时,我也注意到了这一点。通过将每个“案例”括在括号中可以轻松避免这种情况。 (2认同)

gar*_*ing 9

  const [route, setRoute] = useState(INITIAL_ROUTE)

  return (
    <RouteContext.Provider value={{ route, setRoute }}>
      {(() => {
        switch (route) {
          case Route.Home:
            return <PopupHomePage />
          case Route.App:
            return <PopupAppPage />
          default:
            return null
        }
      })()}
    </RouteContext.Provider>
Run Code Online (Sandbox Code Playgroud)


A.c*_*com 7


function Notification({ text, status }) {
  return (
    <div>
      {(() => {
        switch (status) {
          case 'info':
            return <Info text={text} />;
          case 'warning':
            return <Warning text={text} />;
          case 'error':
            return <Error text={text} />;
          default:
            return null;
        }
      })()}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,这是每个人真正寻找的答案。 (2认同)

mon*_*yjs 6

你可以做这样的事情。

 <div>
          { object.map((item, index) => this.getComponent(item, index)) }
 </div>

getComponent(item, index) {
    switch (item.type) {
      case '1':
        return <Comp1/>
      case '2':
        return <Comp2/>
      case '3':
        return <Comp3 />
    }
  }
Run Code Online (Sandbox Code Playgroud)


ABC*_*.ca 6

您无法在渲染中进行切换。放置访问一个元素的对象文字的伪切换方法并不理想,因为它会导致所有视图都进行处理,并且可能导致该状态下不存在的 props 的依赖错误。

\n\n

这是一种非常干净的方法,不需要提前渲染每个视图:

\n\n
render () {\n  const viewState = this.getViewState();\n\n  return (\n    <div>\n      {viewState === ViewState.NO_RESULTS && this.renderNoResults()}\n      {viewState === ViewState.LIST_RESULTS && this.renderResults()}\n      {viewState === ViewState.SUCCESS_DONE && this.renderCompleted()}\n    </div>\n  )\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果视图状态的条件不仅仅基于简单的属性 \xe2\x80\x93 (例如每行多个条件),那么枚举和getViewState封装条件的函数是分离此条件逻辑并清理渲染的好方法。

\n


fie*_*dju 6

我真的很喜欢/sf/answers/4221949931/中的建议,所以我将其改编为 Typescript,如下所示

import React, { FunctionComponent } from 'react'
import { Optional } from "typescript-optional";
const { ofNullable } = Optional

interface SwitchProps {
  test: string
  defaultComponent: JSX.Element
}

export const Switch: FunctionComponent<SwitchProps> = (props) => {
  return ofNullable(props.children)
    .map((children) => {
      return ofNullable((children as JSX.Element[]).find((child) => child.props['value'] === props.test))
        .orElse(props.defaultComponent)
    })
    .orElseThrow(() => new Error('Children are required for a switch component'))
}

const Foo = ({ value = "foo" }) => <div>foo</div>;
const Bar = ({ value = "bar" }) => <div>bar</div>;
const value = "foo";
const SwitchExample = <Switch test={value} defaultComponent={<div />}>
  <Foo />
  <Bar />
</Switch>;
Run Code Online (Sandbox Code Playgroud)


小智 6

import React from 'react';

import ListView from './ListView';
import TableView from './TableView';

function DataView({
    currView,
    data,
    onSelect,
    onChangeStatus,
    viewTodo,
    editTodo,
    deleteTodo,
}) {
    return (
        <div>
            {(function () {
                switch (currView) {
                    case 'table':
                        return (
                            <TableView
                                todos={data}
                                onSelect={onSelect}
                                onChangeStatus={onChangeStatus}
                                viewTodo={viewTodo}
                                editTodo={editTodo}
                                deleteTodo={deleteTodo}
                            />
                        );

                    case 'list':
                        return (
                            <ListView
                                todos={data}
                                onSelect={onSelect}
                                onChangeStatus={onChangeStatus}
                                viewTodo={viewTodo}
                                editTodo={editTodo}
                                deleteTodo={deleteTodo}
                            />
                        );

                    default:
                        break;
                }
            })()}
        </div>
    );
}

export default DataView;
Run Code Online (Sandbox Code Playgroud)


Kam*_*rna 6

比马特·韦的回答有所改进 。

export const Switch = ({ test, children }) => {
  const defaultResult = children.find((child) => child.props.default) || null;
  const result = children.find((child) => child.props.value === test);

  return result || defaultResult;
};
export const Case = ({ children }) => children;


const color = getColorFromTheMostComplexFnEver();

<Switch test={color}>
  <Case value="Green">Forest</Case>
  <Case value="Red">Blood</Case>
  <Case default>Predator</Case>
</Switch>
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Run Code Online (Sandbox Code Playgroud)


wil*_*msi 5

我在render()方法中做到了这一点:

  render() {
    const project = () => {
      switch(this.projectName) {

        case "one":   return <ComponentA />;
        case "two":   return <ComponentB />;
        case "three": return <ComponentC />;
        case "four":  return <ComponentD />;

        default:      return <h1>No project match</h1>
      }
    }

    return (
      <div>{ project() }</div>
    )
  }
Run Code Online (Sandbox Code Playgroud)

我试图使render()返回的内容保持整洁,因此我将逻辑放在上面的'const'函数中。这样,我也可以整齐地缩进开关盒。