315

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?

typos
  • 5,932
  • 13
  • 40
  • 52
  • 1
    Well, you don't need to have all that logic in the `return` statement or even the `render` method for that matter. Could you define each `
    ` as a const, and then use the `switch` *before* your `return` to determine which `
    ` should be rendered?
    – Jared Goguen Oct 05 '17 at 18:58
  • @JaredGoguen But then, I would need to repeat the `div` at the top and bottom, multiple times for each case of the `switch`. Or I just misunderstood, you.. – typos Oct 05 '17 at 18:59
  • no, you could create code for `let middleDiv = ...` and then include `{middleDiv}` in your return JSX between the two `
    `s that you have hard-coded there.
    – Jared Goguen Oct 05 '17 at 19:00

29 Answers29

450

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>
  );
}
Dhia Djobbi
  • 1,176
  • 2
  • 15
  • 35
Kelvin De Moya
  • 5,118
  • 1
  • 17
  • 16
  • 2
    If you put the function call in the return it always calls on rendering. So, if you need to call renderSwitch from somewhere else other than the return statement this won't work. – London804 Jan 10 '22 at 22:12
  • 1
    Furthermore, it gets called multiple times. – London804 Jan 10 '22 at 22:54
305

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>
  )
}
lenkan
  • 4,089
  • 1
  • 14
  • 12
  • 2
    this is pretty cool. I modified it slightly to use an array instead of a POJO and I'm just using the index to bracket into the array. – Nicholas Gentile Jul 23 '18 at 18:19
  • 2
    This is the standard way of handling switch-case in Python. I like it better in this instance because of its superior readability. – I'll Eat My Hat Jan 16 '19 at 19:04
  • 42
    This approach has its limits and overhead. Each of your views will be processed and will depend on the current state/props which might not exist. Ex: lets say you wanted to either render: `` or ``. If the view state should render ``, `` might not compile because it depends on properties that don't yet exist. – ABCD.ca Apr 09 '19 at 18:37
  • 1
    @ABCD.ca what do you mean by processed? Only the rendered component will be instantiated. The above is equivalent to writing React.createElement(Foo), which does not instantiate the component. – lenkan Apr 11 '19 at 15:56
  • 1
    Hah I chose that word to be vague because tbh I don't know what to call it. I know it won't get rendered but code from one of the states I didn't want to render was still being executed. I could tell because for me, going the object literal map way, I found i got compile errors for missing props — props that only end up existing once I'm in the correct state. – ABCD.ca Apr 12 '19 at 04:16
  • @NicholasGentile How did you do this with array and index? – Josef Oct 04 '19 at 18:52
  • 16
    What about a default case? – kungfooman Jan 05 '20 at 12:09
  • 38
    @lama12345 For default case, use `||` as follows: `{ 'foo': , 'bar': }[param] || ` – Aaron Adrian Apr 02 '20 at 05:29
  • @Josef TLDR; I created a `const` for each tab and assigned it to a number. The menu tabs array contained tab components so based on the `activeTab` variable in the menu state, it would render that tab component based on the index. This is not great because its brittle, but it wasn't an actively maintained codebase so it worked as an experiment. `[, , , ...][activeTab]`. One way to do it, not the best way! The best solution is to use an object like the OP solutionist did. – Nicholas Gentile May 03 '20 at 19:08
  • Is it possible to pass down this to such a javascript object? Looking to pass props to the selected component @lenkan – Benjamin Jun 02 '20 at 07:35
  • 3
    This looks awesome! Any idea how to use enums as the keys for this object? I.e.: `MyEnum.Foo: , MyEnum.Bar: ` My compiler complains about an unexpected token with the dot in between `MyEnum` and `Foo` – Ofek Gila Aug 07 '20 at 20:43
  • 2
    Incredible how many insanely complicated solutions people have proposed instead of simply doing this – Jehan Mar 19 '21 at 13:59
  • What's the name of this operator? It does not work properly with **TypeScript**. – SalahAdDin Jun 20 '21 at 10:57
  • This won't work with TypeScript if the switch was previously being used as a type guard. – Richard Jan 11 '22 at 13:29
  • This is really cool, and I had not thought about it. @ABCD.ca A switch case is to be used when you only want 1 answer. So that is not really a detraction in the context of this question. – Martin Rojas May 13 '22 at 20:06
  • This is a nice way to do this, just wanted to comment and say thanks for sharing! – Anthony Jul 12 '22 at 21:38
  • 3
    @OfekGila same as defining any weird object keys, so like [MyEnum.Foo]: – Shautieh Sep 09 '22 at 18:24
  • Completely untype checked. – dessalines May 22 '23 at 16:15
105

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>
    );
}
1ven
  • 6,776
  • 1
  • 25
  • 39
  • Thanks I used that like: render () { return (
    {(() => { switch (this.state.type) { case commons.HARD_SOFT: return ; } })()}
    );
    – JhonQO Apr 27 '18 at 15:33
  • 9
    Why is it not recommended? – bluenote10 Nov 25 '20 at 22:35
  • 7
    Likely he read something somewhere that says not to do it because it's ugly to someone somehow. A lot of people don't like switch statements for varying reasons. While it's not mentioned in the React documentation, conditional rendering is obviously supported by React, and switch statements don't cause any issues with React. – William Jan 12 '21 at 01:19
  • How would this work? I get a reference error tab is not defined for my switch statement. – London804 Jan 10 '22 at 22:17
56

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.

ogostos
  • 1,435
  • 2
  • 20
  • 29
williamsi
  • 1,498
  • 1
  • 15
  • 15
  • @a_m_dev Instead of a 'const project' function inside the render method, we can place it as a component method, then call it inside render return like "
    { this.project() }
    ". Unless you're talking about not using switch at all, then I can think of using if / else, or show / hide components using className by updating the state.
    – williamsi Apr 10 '19 at 19:10
  • that could be good even more , because for example i use a `head()` method of my route components to inject the data by `react-helmet` to the head of my document – amdev Apr 11 '19 at 13:38
  • I use this solution with a functional component, and it works. Good job. – quine9997 Nov 13 '21 at 11:44
44

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.

Matt Way
  • 32,319
  • 10
  • 79
  • 85
  • 11
    Exactly what I was looking for! Here is a little improvement: ```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 ! }; ``` That way you can write: ```javascript ``` – lsmod Mar 30 '20 at 13:24
  • While this is a good solution, adding an explanation to the code would have made it even better – papigee Oct 22 '20 at 22:57
  • @papigee Updated with slightly more detail. – Matt Way Oct 23 '20 at 01:03
  • @MattWay While this a good option for pure JS, it throws a TS error when target is set to es5 as property find does not exist on ReactNode – Zakriya Bilal Oct 25 '20 at 12:58
  • @ZakriyaBilal The code is more of a proof of concept, and about how componentising ideas like switches will make your code more react like. The key is just finding a way to compare the props of children. – Matt Way Oct 26 '20 at 02:56
  • 4
    TypeScript version of this at https://stackoverflow.com/a/63852247/733092. – Noumenon Feb 28 '21 at 01:04
  • thank you very much! I like this approach – CasperCarver Jan 19 '22 at 07:14
  • Takes too long to read – A.com Feb 08 '22 at 01:23
34

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.

arvymetal
  • 2,787
  • 1
  • 30
  • 39
  • 3
    Nice and elegant and can be used right in the render block. Best answer IMHO – GavinBelson Apr 03 '20 at 23:14
  • 3
    I noticed this is a violation of EsLints rules https://eslint.org/docs/rules/no-mixed-operators mixing && and || – Faesel Saeed Aug 13 '20 at 16:11
  • 3
    @FishFingers I noticed that too when I tried to use it exactly as above. It can be easily avoided by wrapping each "case" in parentheses. – Ch0sen Aug 14 '20 at 23:37
  • 1
    Are there any benefits of this over a plain switch? Switch is cleaner and easy to see what goes where, this is pretty convoluted to do the same thing IMO. Where is the next && and then ||, what happens if OR this AND that.. – James Feb 15 '21 at 13:01
  • @James that's a method whose state of mind, and consequently benefits, are the same as for "Inline If with conditional operator" mentioned by the React documentation: https://reactjs.org/docs/conditional-rendering.html#inline-if-with-logical--operator. I'd even say it's simply an extension of it. Preferring to separate some rendering logic from the render block is a subjective choice, and proficiency with conditional operators is something personal. – arvymetal Mar 12 '21 at 04:48
30

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)
Tom Spencer
  • 7,816
  • 4
  • 54
  • 50
25

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
  }
}
Luke Burns
  • 1,911
  • 3
  • 24
  • 30
  • 7
    {{key1: , ...}[key] is not a good solution. You see, before the selection happens, the whole initial object is constructed - i.e. every branch of the switch is rendered - Component1, Component2, etc... – Gleb Varenov Jul 12 '19 at 23:48
  • Yeah, lenkan's answer should be the correct answer, because switch shouldn't be used in functional component. Thanks for adding *OR* for default case. And dont bother with rswitch(), the map solution is spot on! *thumbs up* – Eugenijus S. Sep 05 '19 at 12:17
17

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>
  );
}
A.com
  • 1,466
  • 4
  • 19
  • 31
13

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 />
    }
  }
monkeyjs
  • 604
  • 2
  • 7
  • 29
6

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.

ABCD.ca
  • 2,365
  • 3
  • 32
  • 24
6

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>;
fieldju
  • 1,443
  • 1
  • 14
  • 20
  • Great. Now add `` like in https://stackoverflow.com/questions/46592833/how-to-use-switch-statement-inside-a-react-component#comment107799882_60313570 and publish it as a npm package :-) – nachtigall Mar 03 '21 at 08:58
6
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;
6

Improved a bit from Matt Way's answer.

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>
Kamil Sarna
  • 5,993
  • 1
  • 32
  • 22
4

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>
   );
}
James
  • 1,100
  • 1
  • 13
  • 29
4

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>
Ayoub Benayache
  • 1,046
  • 12
  • 28
4

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>
Ashish Tripathi
  • 580
  • 4
  • 18
3

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>
  );
Rifat
  • 1,700
  • 3
  • 20
  • 51
3

We can do this directly using 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]);

This is to be used inside the jsx

<div>Sort:{renderContent()}</div>
champion-runner
  • 1,489
  • 1
  • 13
  • 26
3
  const [route, setRoute] = useState(INITIAL_ROUTE)

  return (
    <RouteContext.Provider value={{ route, setRoute }}>
      {(() => {
        switch (route) {
          case Route.Home:
            return <PopupHomePage />
          case Route.App:
            return <PopupAppPage />
          default:
            return null
        }
      })()}
    </RouteContext.Provider>
lovelikelando
  • 7,593
  • 6
  • 32
  • 50
2

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.

jerryurenaa
  • 3,863
  • 1
  • 27
  • 17
2

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

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>
Tudor Morar
  • 3,720
  • 2
  • 27
  • 25
1

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;
}
Niklas
  • 49
  • 3
1

Alternative: use operators. They dont suffer the same JSX limitations that statements such as 'switch' do.

const Page = () => {
    const [currentView, setCurrentView] = useState('home');

    return <>
        <AppBar />
        {
            currentView === 'home'
            && <HomePage />

            || currentView === 'login'
            && <LoginPage />

            || currentView === 'register'
            && <RegisterPage />

            || <DefaultPage />
        }
    </>;
};
Edan Kriss
  • 51
  • 3
0

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})`) }
Cat
  • 4,141
  • 2
  • 10
  • 18
0

Is easy my example in typescrip, change page on object in state

// add names of pages (safe) or change to string (unsafe)
type pages = 'bip' | 'boop'

const App = (params: params) => {

     
    const [menu, setMenu] = useState<pages>("bip")

    const handleOnClick = (value: string) => setMenu(value);

    const pages: { [key: string]: React.ReactNode } = {
        bip: <div>bip</div>,
        boop: <div>Boop</div>
    }

    return (
        <>

            {pages[menu] ? pages[menu] : <DefaultComponent/>}

            <OtherComponent onClick={handleOnClick} />

        </>
    );
};

export default App;
0

A elegant way is to use an array index as dependency:

<Box>
    {
      {
        0: <ComponentOne/>,
        1: <ComponentTwo/>,
        2: <ComponentThree/>,
      }[arrayIndex]         // arrayIndex as dependency
    }
</Box>
Java bee
  • 2,522
  • 1
  • 12
  • 25
-2

This is another approach.

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

renderStep0() { return 'step 0' }
renderStep1() { return 'step 1' }
entpnerd
  • 10,049
  • 8
  • 47
  • 68