Wie verwende ich die switch-Anweisung in einer React-Komponente?

101

Ich habe eine React-Komponente und innerhalb der renderMethode der Komponente habe ich ungefähr Folgendes:

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

           { switch(...) {} }

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

Jetzt geht es darum, dass ich zwei divElemente habe, eines oben und eines unten, die fest sind. In der Mitte möchte ich eine switch-Anweisung haben und gemäß einem Wert in meinem Status möchte ich eine andere Komponente rendern. Grundsätzlich möchte ich, dass die beiden divElemente immer fixiert werden und nur in der Mitte jedes Mal eine andere Komponente gerendert wird. Ich verwende dies, um ein mehrstufiges Zahlungsverfahren zu implementieren. Obwohl der Code derzeit nicht funktioniert, funktioniert er nicht, da ich einen Fehler bekomme, der besagt, dass dies switchunerwartet ist. Irgendwelche Ideen, wie ich das erreichen kann, was ich will?

Tippfehler
quelle
Nun, Sie müssen nicht all diese Logik in der returnAnweisung oder sogar der renderMethode für diese Angelegenheit haben. Könnten Sie jede <div>als Konstante definieren und dann die switch vor Ihrer verwenden return, um zu bestimmen, welche <div>gerendert werden soll?
Jared Goguen
@JaredGoguen Aber dann müsste ich divdas oben und unten mehrmals für jeden Fall des wiederholen switch. Oder ich habe es einfach falsch verstanden, Sie ..
Tippfehler
Nein, Sie können Code für JSX erstellen let middleDiv = ...und dann {middleDiv}zwischen den beiden <div>s, die Sie dort fest codiert haben, in Ihre Rückgabe aufnehmen .
Jared Goguen

Antworten:

184

Versuchen Sie dies, was auch viel sauberer ist: Nehmen Sie diesen Schalter in einer Funktion aus dem Render heraus und rufen Sie ihn einfach auf, indem Sie die gewünschten Parameter übergeben. Beispielsweise:

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

Kelvin De Moya
quelle
81

Im Gegensatz zu anderen Antworten würde ich es vorziehen, den "Schalter" in der Renderfunktion einzubinden. Dadurch wird klarer, welche Komponenten an dieser Position gerendert werden können. Sie können einen switch-ähnlichen Ausdruck implementieren, indem Sie ein einfaches altes Javascript-Objekt verwenden:

render () {
  return (
    <div>
      <div>
        {/* removed for brevity */}
      </div>
      {
        {
          'foo': <Foo />,
          'bar': <Bar />
        }[param]
      }
      <div>
        {/* removed for brevity */}
      </div>
    </div>
  )
}
Lenkan
quelle
1
das ist ziemlich cool. Ich habe es leicht modifiziert, um ein Array anstelle eines POJO zu verwenden, und ich verwende nur den Index, um in das Array einzuklammern.
Nicholas Gentile
1
Dies ist die Standardmethode für den Umgang mit Switch-Case in Python. Ich mag es in diesem Fall besser wegen seiner überlegenen Lesbarkeit.
Ich werde meinen Hut am
11
Dieser Ansatz hat seine Grenzen und seinen Aufwand. Jede Ihrer Ansichten wird verarbeitet und hängt vom aktuellen Status / den aktuellen Requisiten ab, die möglicherweise nicht vorhanden sind. Beispiel: Angenommen, Sie wollten entweder rendern: <SearchResults />oder <NoResults />. Wenn der Ansichtsstatus gerendert werden soll, <NoResults />wird er <SearchResults />möglicherweise nicht kompiliert, da er von Eigenschaften abhängt, die noch nicht vorhanden sind.
ABCD.ca
2
Was ist mit einem Standardfall?
lama12345
2
@ lama12345 Verwenden Sie für den Standardfall ||Folgendes:{ 'foo': <Foo />, 'bar': <Bar /> }[param] || <Baz />
Aaron Adrian
49

Das passiert, weil switchAnweisung eine ist statement, aber hier erwartet Javascript einen Ausdruck.

Obwohl es nicht empfohlen wird, die switch-Anweisung in einer renderMethode zu verwenden, können Sie die selbstaufrufende Funktion verwenden, um dies zu erreichen:

render() {
    // Don't forget to return a value in a switch statement
    return (
        <div>
            {(() => {
                switch(...) {}
            })()}
        </div>
    );
}
1ven
quelle
Danke, ich habe das wie folgt verwendet: render () {return (<div> {(() => {switch (this.state.type) {case commons.HARD_SOFT: return <HardSoft params = {this.state.param} onSubmitHead = {this.onSubmit} />;}}) ()} </ div>);
JhonQO
19

Ich habe dies innerhalb der render () -Methode getan:

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

Ich habe versucht, die Rückgabe von render () sauber zu halten, also habe ich meine Logik in eine 'const'-Funktion direkt darüber eingefügt. Auf diese Weise kann ich auch meine Schaltergehäuse sauber einrücken.

williamsi
quelle
@a_m_dev Anstelle einer 'const project'-Funktion in der Render-Methode können wir sie als Komponentenmethode platzieren und dann in render return wie "<div> {this.project ()} </ div>" aufrufen. Wenn Sie nicht davon sprechen, überhaupt keinen Schalter zu verwenden, kann ich mir vorstellen, if / else zu verwenden oder Komponenten mithilfe von className anzuzeigen / auszublenden, indem Sie den Status aktualisieren.
Williamsi
das könnte noch mehr gut sein, weil ich zum Beispiel eine head()Methode meiner react-helmet
Routenkomponenten verwende
15

Eine Möglichkeit, eine Art Schalter in einem Renderblock mithilfe von bedingten Operatoren darzustellen :

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

Es sollte sichergestellt werden, dass die Bedingungen strikt einen Booleschen Wert zurückgeben.

Arvymetall
quelle
2
Schön und elegant und kann direkt im Renderblock verwendet werden. Beste Antwort IMHO
GavinBelson
2
Ich habe festgestellt, dass dies einen Verstoß gegen die EsLints-Regeln darstellt. Eslint.org/docs/rules/no-mixed-operators mixing && und ||
FishFingers
1
@FishFingers Das habe ich auch bemerkt, als ich versucht habe, es genau wie oben zu verwenden. Es kann leicht vermieden werden, indem jeder "Fall" in Klammern gesetzt wird.
Ch0sen
14

lenkans antwort ist eine großartige lösung.

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

Wenn Sie einen Standardwert benötigen, können Sie dies sogar tun

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

Wenn Ihnen das nicht gut gefällt, können Sie alternativ so etwas tun

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

mit

function rswitch (param, cases) {
  if (cases[param]) {
    return cases[param]
  } else {
    return cases.default
  }
}
Luke Burns
quelle
2
{{key1: <Component1 />, ...} [key] ist keine gute Lösung. Sie sehen, bevor die Auswahl erfolgt, wird das gesamte ursprüngliche Objekt erstellt - dh jeder Zweig des Schalters wird gerendert - Komponente1, Komponente2 usw.
Gleb Varenov
Ja, die Antwort von Lenkan sollte die richtige sein, da der Schalter nicht in der Funktionskomponente verwendet werden sollte. Vielen Dank für das Hinzufügen von OR für den Standardfall. Und kümmern Sie sich nicht um rswitch (), die Kartenlösung ist genau richtig! Daumen hoch
Eugenijus S.
8

Ich bin kein großer Fan einer der aktuellen Antworten, weil sie entweder zu ausführlich sind oder Sie den Code durchspringen müssen, um zu verstehen, was los ist. Warum nicht stattdessen eine wirklich einfache Switch-Komponente erstellen, die basierend auf einem Requisitentest bedingt gerendert wird? Beispielsweise:

const Switch = props => {
  const { test, children } = props
  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>

Sie können den Wechsel so einfach oder so komplex gestalten, wie Sie möchten. Vergessen Sie nicht, die Kinder und ihre Wertschöpfungen genauer zu überprüfen.

Matt Way
quelle
2
Genau das, wonach ich gesucht habe! Hier ist eine kleine Verbesserung: Auf 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 ! }; diese Weise können Sie schreiben:javascript <Switch test={true}> <Case value={true}> <ItAlwaysFeelsRightToBeTrue /> </Case> <Case value={false}> <FalseAlarm /> </Case> </Switch>
lsmod
3

Wie wäre es mit:

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

Sie können so etwas tun.

 <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 />
    }
  }
Affenjs
quelle
2

Sie können keinen Schalter im Render haben. Der Pseudo-Switch-Ansatz, ein Objektliteral zu platzieren, das auf ein Element zugreift, ist nicht ideal, da alle Ansichten verarbeitet werden und dies zu Abhängigkeitsfehlern von Requisiten führen kann, die in diesem Zustand nicht vorhanden sind.

Hier ist eine schöne, saubere Methode, bei der nicht jede Ansicht im Voraus gerendert werden muss:

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

Wenn Ihre Bedingungen, für die der Ansichtsstatus verwendet wird, auf mehr als einer einfachen Eigenschaft basieren - wie mehrere Bedingungen pro Zeile -, sind eine Aufzählung und eine getViewStateFunktion zum Kapselen der Bedingungen eine gute Möglichkeit, diese bedingte Logik zu trennen und Ihr Rendering zu bereinigen.

ABCD.ca
quelle
Einfach und sauber.
Abhilekh Singh
2

Hier ist ein voll funktionsfähiges Beispiel mit einer Schaltfläche zum Wechseln zwischen Komponenten

Sie können einen Konstruktor wie folgt festlegen

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

Anschließend können Sie Komponenten wie folgt rendern

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

}}

Wie Sie sehen können, benutze ich eine Taste, um zwischen den Zuständen zu wechseln.

jerryurenaa
quelle
0

Dies ist ein anderer Ansatz.

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

renderStep0() { return 'step 0' }
renderStep1() { return 'step 1' }
Rodrigo Hernan Ramos
quelle
0

Der Vorschlag in https://stackoverflow.com/a/60313570/770134 hat mir sehr gut gefallen , daher habe ich ihn wie folgt an Typescript angepasst

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
quelle