【问题标题】:How to use switch statement inside a React component?如何在 React 组件中使用 switch 语句?
【发布时间】: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 中。

标签: reactjs


【解决方案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 上不存在属性查找
                • stackoverflow.com/a/63852247/733092 的 TypeScript 版本。
                【解决方案12】:

                我真的很喜欢 https://stackoverflow.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>;
                

                【讨论】:

                【解决方案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 />
                          }
                        }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2021-12-10
                        • 2020-11-09
                        • 1970-01-01
                        • 2021-03-28
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-07-19
                        • 2022-01-23
                        相关资源
                        最近更新 更多