How to use switch statement inside a React component?

Reactjs

Reactjs Problem Overview


I have a React component, and inside the render method of the component I have something like this:

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

           { switch(...) {} }
            
            <div>
                // removed for brevity
            </div>
        </div>
    );
}

Now the point is that I have two div elements, one at the top and one at the bottom, that are fixed. In the middle I want to have a switch statement, and according to a value in my state I want to render a different component. So basically, I want the two div elements to be fixed always, and just in the middle to render a different component each time. I'm using this to implement a multi-step payment procedure). Though, as is the code currently it doesn't work, as it gives me an error saying that switch is unexpected. Any ideas how to achieve what I want?

Reactjs Solutions


Solution 1 - Reactjs

Try this, which is way cleaner too: Get that switch out of the render in a function and just call it passing the params you want. For example:

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

Solution 2 - Reactjs

In contrast to other answers, I would prefer to inline the "switch" in the render function. It makes it more clear what components can be rendered at that position. You can implement a switch-like expression by using a plain old javascript object:

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

Solution 3 - Reactjs

That's happening, because switch statement is a statement, but here javascript expects an expression.

Although, it's not recommended to use switch statement in a render method, you can use self-invoking function to achieve this:

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

Solution 4 - Reactjs

I did this inside the render() method:

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

I tried to keep the render() return clean, so I put my logic in a 'const' function right above. This way I can also indent my switch cases neatly.

Solution 5 - Reactjs

I'm not a big fan of any of the current answers, because they are either too verbose, or require you to jump around the code to understand what is going on.

I prefer doing this in a more react component centred way, by creating a <Switch/>. The job of this component is to take a prop, and only render children whose child prop matches this one. So in the example below I have created a test prop on the switch, and compared it to a value prop on the children, only rendering the ones that match.

Example:

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>

You can make the switch as simple or as complex as you want. Don't forget to perform more robust checking of the children and their value props.

Solution 6 - Reactjs

A way to represent a kind of switch in a render block, using conditional operators:

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

It should be ensured that the conditions strictly return a boolean.

Solution 7 - Reactjs

lenkan's answer is a great solution.

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

If you need a default value, then you can even do

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

Alternatively, if that doesn't read well to you, then you can do something like

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

with

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

Solution 8 - Reactjs


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

Solution 9 - Reactjs

You can do something like this.

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

Solution 10 - Reactjs

Although this is yet another way to do it, if you have gone all-in on hooks, you could take advantage of useCallback to produce a function that is only recreated when necessary.

Let's say you have a component which should be rendered according to a status prop. With hooks, you could implement this as follows:

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

I like this because:

  • It's obvious what is going on - a function is created, and then later called (the immediately invoked anonymous function method looks a little odd, and can potentially confuse newer developers)
  • The useCallback hook ensures that the renderContent callback is reused between renders, unless the depedency status changes
  • The renderContent function uses a closure to access the necessary props passed in to the component. A separate function (like the accepted answer) requires the passing of the props into it, which can be burdensome (especially when using TypeScript, as the parameters should also be typed correctly)

Solution 11 - Reactjs

How about:

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

Solution 12 - Reactjs

You can't have a switch in render. The psuedo-switch approach of placing an object-literal that accesses one element isn't ideal because it causes all views to process and that can result in dependency errors of props that don't exist in that state.

Here's a nice clean way to do it that doesn't require each view to render in advance:

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

If your conditions for which view state are based on more than a simple property – like multiple conditions per line, then an enum and a getViewState function to encapsulate the conditions is a nice way to separate this conditional logic and cleanup your render.

Solution 13 - Reactjs

Switch-Case statement within React Component could be used as follows:

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

Solution 14 - Reactjs

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;

Solution 15 - Reactjs

I really liked the suggestion in https://stackoverflow.com/a/60313570/770134, so I adapted it to Typescript like so

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

Solution 16 - Reactjs

make it easy and just use many if statements.

for example:

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

Solution 17 - Reactjs

Here is a full working example using a button to switch between components

you can set a constructor as following

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

then you can render components as following

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

}

As you can see I am using a button to switch between states.

Solution 18 - Reactjs

I converted accepted answer to arrow functional component solution and saw James provides similar answer and one can get error not defined. So here is the solution:

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

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

      {renderSwitch(param)}

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

Solution 19 - Reactjs

I know I'm a bit late to the party, but I think this implementation might help

You can render the components using conditional operators instead

If you had the following switch statement

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

    case CASE2:
        return <Case2Component/>

    case CASE3:
        return <Case3Component/>

    default:
        return <DefaultComponent/>
}

You can convert it to react component like so

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

Solution 20 - Reactjs

This helper should do the trick.

Example Usage:

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

Helper:

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

Solution 21 - Reactjs

I am using this helper that allows me to have switch statements in 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>

Solution 22 - Reactjs

This answer is specifically intended to address this "duplicate" question, by @tonyfat, regarding how to use conditional expressions to handle the same task.


Avoiding statements here seems like more trouble than it's worth, but this script does the job as the snippet demonstrates:

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

Solution 23 - Reactjs

This is another approach.

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontyposView Question on Stackoverflow
Solution 1 - ReactjsKelvin De MoyaView Answer on Stackoverflow
Solution 2 - ReactjslenkanView Answer on Stackoverflow
Solution 3 - Reactjs1venView Answer on Stackoverflow
Solution 4 - ReactjswilliamsiView Answer on Stackoverflow
Solution 5 - ReactjsMatt WayView Answer on Stackoverflow
Solution 6 - ReactjsarvymetalView Answer on Stackoverflow
Solution 7 - ReactjsLuke BurnsView Answer on Stackoverflow
Solution 8 - ReactjsA.comView Answer on Stackoverflow
Solution 9 - ReactjsmonkeyjsView Answer on Stackoverflow
Solution 10 - ReactjsTom SpencerView Answer on Stackoverflow
Solution 11 - ReactjsJamesView Answer on Stackoverflow
Solution 12 - ReactjsABCD.caView Answer on Stackoverflow
Solution 13 - ReactjsAshish TripathiView Answer on Stackoverflow
Solution 14 - ReactjsMuhammad Sultan Al MahfuzView Answer on Stackoverflow
Solution 15 - ReactjsfieldjuView Answer on Stackoverflow
Solution 16 - ReactjsAyoub BenayacheView Answer on Stackoverflow
Solution 17 - ReactjsjerryurenaaView Answer on Stackoverflow
Solution 18 - ReactjsRifatView Answer on Stackoverflow
Solution 19 - ReactjstroglodyttoView Answer on Stackoverflow
Solution 20 - ReactjsNiklasView Answer on Stackoverflow
Solution 21 - ReactjsTudor MorarView Answer on Stackoverflow
Solution 22 - ReactjsCatView Answer on Stackoverflow
Solution 23 - ReactjsRodrigo Hernan RamosView Answer on Stackoverflow