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

Posted

技术标签:

【中文标题】如何在 React 组件中使用 switch 语句?【英文标题】:How to use switch statement inside a React component? 【发布时间】:2018-03-17 11:57:36 【问题描述】:

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

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

            switch(...)  

            <div>
                // removed for brevity
            </div>
        </div>
    );

现在的重点是我有两个div 元素,一个在顶部,一个在底部,它们是固定的。在中间我想要一个 switch 语句,并根据我的状态中的一个值,我想渲染一个不同的组件。所以基本上,我希望两个 div 元素始终被固定,并且每次都在中间渲染一个不同的组件。我正在使用它来实施多步骤付款程序)。不过,就像目前的代码一样,它不起作用,因为它给了我一个错误,说switch 是意外的。有什么想法可以实现我想要的吗?

【问题讨论】:

好吧,您不需要在return 语句甚至render 方法中包含所有这些逻辑。您能否将每个&lt;div&gt; 定义为一个常量,然后使用switch before 你的return 来确定应该呈现哪个&lt;div&gt; @JaredGoguen 但是,我需要在顶部和底部重复 div,对于 switch 的每种情况多次重复。或者我只是误会了,你.. 不,您可以为let middleDiv = ... 创建代码,然后将middleDiv 包含在您在那里硬编码的两个&lt;div&gt;s 之间的返回JSX 中。 【参考方案1】:

我正在使用这个助手,它允许我在 JSX 中使用 switch 语句

// in helpers folder
const switchTrue = (object) => 
  const  default: defaultValue, ...rest  = object;
  const obj =  default: defaultValue, ...rest ;
  const result = Object.keys(obj).reduce((acc, cur) => 
    return 
      ...acc,
      [cur === 'default' ? 'true' : cur]: obj[cur],
    ;
  , );
  return result['true'];
;

const Sample = () => 
  const isDataLoading = false;
  return (
    <div>
      
        switchTrue(
          [`$isDataLoading`]: <div>Loading</div>,
          [`$!isDataLoading`]: <div>Data Ready</div>,
          default: <div>Default</div>,
        )
      
    </div>
  )


ReactDOM.render(
  <Sample/>,
  document.getElementById("react")
);
<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>

【讨论】:

【参考方案2】:

虽然这是另一种实现方式,但如果您已经全押,您可以利用useCallback 来生成仅在必要时重新创建的函数。

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

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>
  );
;

我喜欢这个,因为:

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

【讨论】:

【参考方案3】:
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;

【讨论】:

【参考方案4】:

让它变得简单,只需使用许多 if 语句。

例如:

<Grid>
   yourVar==="val1"&&(<> your code for val1 </>)
   yourVar==="val2"&&(<> your code for val2 </>)
   .... other statments
</Grid>

【讨论】:

【参考方案5】:

试试这个,它也更简洁:在函数中将该开关从渲染中取出,然后通过传递您想要的参数调用它。例如:

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>
  );

【讨论】:

如果你把函数调用放在返回中,它总是调用渲染。因此,如果您需要从 return 语句以外的其他地方调用 renderSwitch,这是行不通的。 此外,它被多次调用。【参考方案6】:

我知道我参加聚会有点晚了,但我认为这个实施可能会有所帮助

您可以使用条件运算符来渲染组件

如果你有以下 switch 语句

switch(value) 
    case CASE1:
        return <Case1Component/>

    case CASE2:
        return <Case2Component/>

    case CASE3:
        return <Case3Component/>

    default:
        return <DefaultComponent/>

你可以把它转换成这样的反应组件

const cases = [CASE0, CASE1, CASE2]
// Reminds me of 'react-router-dom'
return (
    <div>
        value === cases[0] && <Case0Component/>
        value === cases[1] && <Case1Component/>
        value === cases[2] && <Case2Component/>
        !cases.includes(value) && <DefaultComponent/>
    </div>
)

【讨论】:

【参考方案7】:

React Component 中的 Switch-Case 语句可以如下使用:

<div  id="time-list">
   
    (() => 
        switch (groupByFilterId) 
            case 0:/*Case 0 */
                return (
                    <div>Case 0</div>
                )
               break;
           case 1: /*Case 1 */
           return ( 
            <div>Case 1</div>
            )
            break;
           case 2:/*Case 2 */
           return ( 
            <div>Case 2</div>
            )
            break;
        
     )()

      
       
    
    </div>

【讨论】:

复制我的答案【参考方案8】:

此答案专门针对@tonyfat 撰写的this "duplicate" question,关于如何使用条件表达式来处理相同的任务。


在这里避免声明似乎比它的价值更麻烦,但这个脚本完成了 sn-p 演示的工作:

// Runs tests
let id = 0, flag = 0;
renderByFlag(id, flag); // jobId out of range

id = 1; // jobId in range
while(++flag < 5) // active flag ranges from 1 to 4
  renderByFlag(id, flag);


// Defines a function that chooses what to render based on two provided values
function renderByFlag(jobId, activeFlag)
  jobId === 1 ? (
      activeFlag === 1
        ? render("A (flag = 1)")
        : activeFlag === 2
          ? render("B (flag = 2)")
          : activeFlag === 3
            ? render("C (flag = 3)")
            : pass(`flag $activeFlag out of range`)
  )
  : pass(`jobId $jobId out of range`)


// Defines logging functions for demo purposes
function render(val) console.log(`Rendering $val`); 
function pass(reason) console.log(`Doing nothing ($reason)`) 

【讨论】:

我同意这是一个更好更简洁的语法,如果命名/约定不是很清楚,你也可以使用 cmets。 最不清晰的选项【参考方案9】:

我将接受的答案转换为箭头功能组件解决方案,并看到 James 提供了类似的答案,并且可能会收到错误 not defined。所以这里是解决方案:

  const renderSwitch = (param) => 
    switch (param) 
      case "foo":
        return "bar";
      default:
        return "foo";
    
  ;

  return (
    <div>
      <div></div>

      renderSwitch(param)

      <div></div>
    </div>
  );

【讨论】:

【参考方案10】:

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>
  );

【讨论】:

【参考方案11】:

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

我更喜欢通过创建&lt;Switch/&gt; 以更加以反应组件为中心的方式来执行此操作。该组件的工作是获取一个道具,并且只渲染子道具与该道具匹配的孩子。因此,在下面的示例中,我在开关上创建了一个 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")
);
<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>

您可以根据需要使切换变得简单或复杂。不要忘记对孩子及其价值道具执行更稳健的检查。

【讨论】:

正是我想要的!这里有一点改进:javascript const Switch = props =&gt; const test, children = props; return children.find(child =&gt; return child.props.value === test; ); ; const Case = ( children, value ) =&gt; return children; // I don't want do add container around my cases ! ; 这样你就可以写: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;/Case&gt; &lt;/Switch&gt; 虽然这是一个很好的解决方案,但在代码中添加解释会更好 @papigee 更新了一些细节。 @MattWay 虽然这对于纯 JS 来说是一个不错的选择,但当目标设置为 es5 时会引发 TS 错误,因为 ReactNode 上不存在属性查找 ***.com/a/63852247/733092 的 TypeScript 版本。【参考方案12】:

我真的很喜欢 https://***.com/a/60313570/770134 中的建议,所以我把它改编成 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>;

【讨论】:

太棒了。现在在***.com/questions/46592833/… 中添加&lt;Case /&gt; 并将其发布为npm 包:-)【参考方案13】:

一种在渲染块中表示一种开关的方法,使用条件运算符:

(someVar === 1 &&
    <SomeContent/>)
|| (someVar === 2 &&
    <SomeOtherContent />)
|| (this.props.someProp === "something" &&
    <YetSomeOtherContent />)
|| (this.props.someProp === "foo" && this.props.someOtherProp === "bar" &&
    <OtherContentAgain />)
||
    <SomeDefaultContent />

应确保条件严格返回布尔值。

【讨论】:

漂亮优雅,可以直接在渲染块中使用。最佳答案恕我直言 我注意到这违反了 EsLints 规则 eslint.org/docs/rules/no-mixed-operators 混合 && 和 || @FishFingers 当我尝试完全按照上述方式使用它时,我也注意到了这一点。通过将每个“案例”括在括号中可以很容易地避免这种情况。 与普通开关相比,这有什么好处吗? Switch 更干净,很容易看到什么去哪里,这对于做同样的事情 IMO 来说是相当复杂的。下一个 && 然后 || 在哪里,如果 OR 这个 AND 那会发生什么.. @James 这是一种方法,其思想状态以及因此的好处与 React 文档中提到的“带有条件运算符的内联 If”相同:reactjs.org/docs/…。我什至会说它只是它的延伸。喜欢将一些渲染逻辑从渲染块中分离出来是一种主观的选择,而对条件运算符的熟练程度则是个人的事情。【参考方案14】:

这是一个使用按钮在组件之间切换的完整示例

你可以设置如下构造函数

constructor(props)

    super(props);
    this.state=
        currentView: ''
    

那么你可以如下渲染组件

  render() 

    const switchView = () => 

    switch(this.state.currentView) 
    

      case "settings":   return <h2>settings</h2>;
      case "dashboard":   return <h2>dashboard</h2>;

      default:      return <h2>dashboard</h2>
    
  

    return (

       <div>

            <button onClick=(e) => this.setState(currentView: "settings")>settings</button>
            <button onClick=(e) => this.setState(currentView: "dashboard")>dashboard</button>

            <div className="container">
                 switchView() 
            </div>


        </div>
    );

如您所见,我正在使用一个按钮在状态之间切换。

【讨论】:

【参考方案15】:

这是另一种方法。

render() 
   return this[`renderStep$this.state.step`]()

renderStep0()  return 'step 0' 
renderStep1()  return 'step 1' 

【讨论】:

【参考方案16】:

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

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

render () 
  const viewState = this.getViewState();

  return (
    <div>
      viewState === ViewState.NO_RESULTS && this.renderNoResults()
      viewState === ViewState.LIST_RESULTS && this.renderResults()
      viewState === ViewState.SUCCESS_DONE && this.renderCompleted()
    </div>
  )

如果视图状态的条件不仅仅基于一个简单的属性——比如每行多个条件,那么一个枚举和一个 getViewState 函数来封装条件是分离这个条件逻辑并清理你的渲染。

【讨论】:

简单干净的方式。【参考方案17】:

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

<div>
   beep: <div>Beep</div>,
     boop: <div>Boop</div>
  [greeting]
</div>

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

<div>
   beep: <div>Beep</div>,
     boop: <div>Boop</div>
  [greeting] || <div>Hello world</div>
</div>

或者,如果这对您来说不是很好,那么您可以执行类似的操作

<div>
   
    rswitch(greeting, 
      beep: <div>Beep</div>,
      boop: <div>Boop</div>,
      default: <div>Hello world</div>
    ) 
  
</div>

function rswitch (param, cases) 
  if (cases[param]) 
    return cases[param]
   else 
    return cases.default
  

【讨论】:

key1: , ...[key] 不是一个好的解决方案。你看,在选择发生之前,整个初始对象都被构造了——即,开关的每个分支都被渲染——Component1、Component2 等等...... 是的,lenkan 的答案应该是正确的答案,因为 switch 不应该用在功能组件中。感谢您为默认情况添加 OR。并且不要打扰 rswitch(),地图解决方案是正确的! 竖起大拇指【参考方案18】:

我在 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>
    )
  

我试图保持 render() 返回干净,所以我把我的逻辑放在上面的一个“const”函数中。这样我也可以整齐地缩进我的开关盒。

【讨论】:

@a_m_dev 我们可以将它作为组件方法放置在 render 方法中,而不是 'const project' 函数,然后在 render return 中调用它,如 " this.project() ”。除非您说根本不使用 switch,否则我可以考虑使用 if/else,或者通过更新状态使用 className 显示/隐藏组件。 这可能会更好,因为例如我使用路由组件的head() 方法将react-helmet 的数据注入到我的文档头部 我将此解决方案与功能组件一起使用,并且可以正常工作。干得好。【参考方案19】:

与其他答案相比,我更愿意在渲染函数中内联“开关”。它使在该位置可以渲染哪些组件更加清晰。您可以使用普通的旧 javascript 对象来实现类似 switch 的表达式:

render () 
  return (
    <div>
      <div>
        /* removed for brevity */
      </div>
      
        
          'foo': <Foo />,
          'bar': <Bar />
        [param]
      
      <div>
        /* removed for brevity */
      </div>
    </div>
  )

【讨论】:

这很酷。我稍微修改了它以使用数组而不是 POJO,我只是使用索引将括号括入数组中。 这是在 Python 中处理 switch-case 的标准方法。在这种情况下,我更喜欢它,因为它具有出色的可读性。 这种方法有其局限性和开销。您的每个视图都将被处理,并将取决于可能不存在的当前状态/道具。例如:假设您想渲染:&lt;SearchResults /&gt;&lt;NoResults /&gt;。如果视图状态应该呈现&lt;NoResults /&gt;&lt;SearchResults /&gt; 可能无法编译,因为它依赖于尚不存在的属性。 默认情况如何? @lama12345 默认情况下,使用||,如下: 'foo': &lt;Foo /&gt;, 'bar': &lt;Bar /&gt; [param] || &lt;Baz /&gt;【参考方案20】:

怎么样:

mySwitchFunction = (param) => 
   switch (param) 
      case 'A':
         return ([
            <div />,
         ]);
      // etc...
   

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

           this.mySwitchFunction(param) 

          <div>
              // removed for brevity
          </div>
      </div>
   );

【讨论】:

【参考方案21】:

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

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

render() 
    // Don't forget to return a value in a switch statement
    return (
        <div>
            (() => 
                switch(...) 
            )()
        </div>
    );

【讨论】:

谢谢我用过这样的:render () return ( (() => switch (this.state.type) case commons.HARD_SOFT: return ; )() ); 为什么不推荐? 可能他在某个地方读到了一些内容,上面写着不要这样做,因为这对某人来说很丑陋。许多人出于各种原因不喜欢 switch 语句。虽然 React 文档中没有提到它,但 React 显然支持条件渲染,并且 switch 语句不会导致 React 出现任何问题。 这将如何工作?我得到一个参考错误选项卡没有为我的 switch 语句定义。【参考方案22】:

你可以这样做。

 <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 />
    
  

【讨论】:

以上是关于如何在 React 组件中使用 switch 语句?的主要内容,如果未能解决你的问题,请参考以下文章

您是不是需要在switch语句JS中使用默认值[重复]

react-router-dom 中的 Switch 语句始终加载 Home 路由,没有其他路由

如何在 React Native 中使用 switch 进行条件渲染?

React 项目使用 switch ... case ... 语句

React 项目使用 switch ... case ... 语句

React Router Switch不呈现特定组件