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

我有一个React组件,在该组件的render方法中,我有如下内容:

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


{ switch(...) {} }


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

现在的问题是我有两个div元素,一个在顶部,一个在底部,它们是固定的。在中间,我想有一个开关语句,并根据我的状态值,我想呈现一个不同的组件。因此,基本上,我希望两个div元素始终是固定的,并且每次在中间呈现一个不同的组件。我使用它来实现一个多步骤支付过程)。不过,正如目前的代码,它不工作,因为它给我一个错误,说switch是意外的。有什么办法能达到我想要的吗?

586396 次浏览

你可以这样做。

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

试试这个,它也更干净:在一个函数中获得渲染的开关,并调用它传递你想要的参数。例如:

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

这是因为switch语句是statement,但这里javascript期望的是表达式。

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

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

如何:

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

与其他答案相比,我更倾向于在渲染函数中内联“开关”。它使什么组件可以在那个位置呈现更清楚。你可以使用一个简单的javascript对象来实现一个类似switch的表达式:

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

一种在呈现块中使用条件操作符表示某种开关的方法:

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

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

我在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'函数上面。这样我也可以缩进我的开关盒整齐。

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

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

这里有一个很好的干净的方法来做到这一点,不需要每个视图提前渲染:

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函数来封装这些条件是分离条件逻辑和清理呈现的好方法。

这是另一种方法。

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


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

我不太喜欢当前的任何答案,因为它们要么太啰嗦,要么需要您在代码中跳跃才能理解发生了什么。

我更喜欢用一种更以react组件为中心的方式来做这件事,通过创建<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>

您可以根据自己的需要进行简单或复杂的切换。不要忘记对子节点及其值道具执行更健壮的检查。

下面是一个使用按钮在组件之间切换的完整工作示例

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

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

正如你所看到的,我正在使用一个按钮来切换状态。

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

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

我将接受的答案转换为箭头功能组件解决方案,看到詹姆斯提供了类似的答案,可以得到错误not defined。这就是解决方案:

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


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


{renderSwitch(param)}


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

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

例如:

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

这个答案是专门针对这个“duplicate"问题的,通过@tonyfat,关于如何使用条件表达式来处理相同的任务。


在这里避免语句似乎比它的价值更麻烦,但这个脚本可以像代码片段所演示的那样完成工作:

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

React组件中的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>

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

您可以使用条件操作符来呈现组件

如果你有下面的switch语句

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


case CASE2:
return <Case2Component/>


case CASE3:
return <Case3Component/>


default:
return <DefaultComponent/>
}

你可以像这样把它转换成react组件

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

虽然这是这是另一种方法,但如果你已经完全使用了钩子,你可以利用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时,因为参数也应该正确输入)

我正在使用这个帮助器,它允许我在JSX中有开关语句

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

这个helper应该做这个技巧 示例用法:< / >强

{componentSwitch(3, (switcher => switcher
.case(1, () =>
<p>It is one</p>
)
.case(2, () =>
<p>It is two</p>
)
.default(() =>
<p>It is something different</p>
)
))}

助手:

interface SwitchCases<T> {
case: (value: T, result: () => React.ReactNode) => SwitchCases<T>;
default: (result: () => React.ReactNode) => SwitchCases<T>;
}


export function componentSwitch<T>(value: T, cases: (cases: SwitchCases<T>) => void) {


var possibleCases: { value: T, result: () => React.ReactNode }[] = [];
var defaultResult: (() => React.ReactNode) | null = null;


var getSwitchCases: () => SwitchCases<T> = () => ({
case: (value: T, result: () => React.ReactNode) => {
possibleCases.push({ value: value, result });


return getSwitchCases();
},
default: (result: () => React.ReactNode) => {
defaultResult = result;


return getSwitchCases();
},
})
    

// getSwitchCases is recursive and will add all possible cases to the possibleCases array and sets defaultResult.
cases(getSwitchCases());


// Check if one of the cases is met
for(const possibleCase of possibleCases) {
if (possibleCase.value === value) {
return possibleCase.result();
}
}


// Check if the default case is defined
if (defaultResult) {
// Typescript wrongly assumes that defaultResult is always null.
var fixedDefaultResult = defaultResult as (() => React.ReactNode);


return fixedDefaultResult();
}


// None of the cases were met and default was not defined.
return undefined;
}

Matt Way的回答 . < / p >

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

我们可以直接使用useCallback来做到这一点

const renderContent = useCallback(() => {
switch (sortState) {
case 'one':
return 'ONE';
case 'two':
return 'TWO';
case 'three':
return 'THREE';
case 'four':
return 'FOUR';
default:
return 'ONE';
}
}, [sortState]);

这将在jsx

<div>Sort:{renderContent()}</div>