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


108

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

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

           { switch(...) {} }

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

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


1
好吧,您无需在return语句中甚至所有render方法中都包含所有逻辑。您可以将每个变量都定义<div>为const,然后switch 之前使用return来确定<div>应呈现的内容吗?
Jared Goguen '17

@JaredGoguen但是,div在每种情况下,我都需要在顶部和底部重复多次switch。还是我误会了,你..
错别字

不,您可以为其创建代码let middleDiv = ...,然后{middleDiv}<div>其中进行硬编码的两个代码之间包含在返回的JSX中。
Jared Goguen '17

Answers:


199

尝试一下,这也更清洁:在函数中从渲染中获取该开关,然后调用它以传递所需的参数。例如:

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


92

与其他答案相反,我希望在render函数中内联“开关”。这样可以更清楚地知道在该位置可以渲染哪些组件。您可以使用普通的旧javascript对象来实现类似switch的表达式:

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

1
这很酷。我对其进行了少许修改,以使用数组而不是POJO,而我只是使用索引将其括入数组。
Nicholas Gentile

1
这是在Python中处理切换情况的标准方法。在这种情况下,我更喜欢它,因为它具有出色的可读性。
我将

12
这种方法有其局限性和开销。您的每个视图都将被处理,并取决于可能不存在的当前状态/属性。例如:假设您要渲染:<SearchResults /><NoResults />。如果视图状态应渲染<NoResults />,则<SearchResults />可能无法编译,因为它取决于尚不存在的属性。
ABCD.ca,

3
那么默认情况呢?
lama12345

3
@ lama12345对于默认情况,请||按以下方式使用:{ 'foo': <Foo />, 'bar': <Bar /> }[param] || <Baz />
Aaron Adrian

54

之所以会这样,是因为switchstatement是一个statement,但是javascript需要一个表达式。

尽管不建议在render方法中使用switch语句,但是您可以使用自调用函数来实现此目的:

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

谢谢,我用这样的方式:render(){return(<div> {(()=> {switch(this.state.type){case commons.HARD_SOFT:return <HardSoft params = {this.state.param} onSubmitHead = {this.onSubmit} />;}})()} </ div>);
JhonQO

22

我在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内部调用它,例如“ <div> {this.project()} </ div>”。除非您完全不使用switch,否则我可以考虑使用if / else,或者通过更新状态使用className显示/隐藏组件。
williamsi

这可能甚至更好,因为例如,我使用head()路由组件的方法将数据注入react-helmet到文档的开头
a_m_dev

15

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

2
{{key1:<Component1 />,...} [key]不是一个好的解决方案。您会看到,在选择发生之前,整个初始对象
都已

是的,lenkan的答案应该是正确的答案,因为不应在功能组件中使用开关。感谢您为默认情况添加OR。而且不要理会rswitch(),地图解决方案就当场了!竖起大拇指
Eugenijus S.19年

15

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

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

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


2
美观大方,可以在渲染块中直接使用。最佳答案恕我直言
GavinBelson

2
我注意到这违反了EsLints规则eslint.org/docs/rules/no-mixed-operators混合&&和||
FishFingers

1
@FishFingers当我尝试完全如上所述使用它时,我也注意到了。通过将每个“大小写”括在括号中可以轻松避免这种情况。
Ch0sen

13

我不是当前答案的忠实拥护者,因为它们要么太冗长,要么要求您跳过代码以了解正在发生的事情。

我更喜欢通过创建一个以组件为中心的方式来执行此操作<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")
);
<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>

您可以根据需要使开关简单或复杂。不要忘记对孩子及其价值道具进行更强大的检查。


3
正是我想要的!这里有一些改进:javascript const Switch = props => { const { test, children } = props; return children.find(child => { return child.props.value === test; }); }; const Case = ({ children, value }) => { return children; // I don't want do add container around my cases ! }; 您可以这样写:javascript <Switch test={true}> <Case value={true}> <ItAlwaysFeelsRightToBeTrue /> </Case> <Case value={false}> <FalseAlarm /> </Case> </Switch>
lsmod

虽然这是一个很好的解决方案,但在代码中添加说明会使其变得更好
papigee

@papigee更新了更多细节。
Way),

@MattWay虽然这是纯JS的一个不错的选择,但是当目标设置为es5时,它会引发TS错误,因为ReactNode上不存在属性查找
Zakriya Bilal,

@ZakriyaBilal该代码更多地是一种概念证明,以及关于像开关这样的组件化想法如何使您的代码产生更多响应的想法。关键只是找到一种比较儿童道具的方法。
Way),

3

怎么样:

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

2

你可以做这样的事情。

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

2

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

这是一种很好的干净方法,不需要预先渲染每个视图:

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封装条件的函数是分隔此条件逻辑并清理渲染的好方法。


简单干净的方法。
Abhilekh Singh,

2

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

您可以按照以下方式设置构造函数

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

}

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


1

我真的很喜欢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>;

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.