Як використовувати оператор switch всередині компонента React?


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,

@JaredGoguen Але тоді мені потрібно було б повторити divвгорі та внизу, кілька разів для кожного випадку switch. Або я просто неправильно зрозумів, ти ..
друкарські помилки

ні, ви можете створити код, let middleDiv = ...а потім включити {middleDiv}у свій повернутий JSX між двома <div>s, які ви там жорстко закодували.
Jared Goguen,

Відповіді:


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


93

На відміну від інших відповідей, я вважаю за краще вбудувати "перемикач" у функцію відтворення. Це робить більш зрозумілим, які компоненти можуть бути відтворені в цій позиції. Ви можете реалізувати вираз, подібний до перемикача, використовуючи звичайний старий об'єкт javascript:

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

1
це досить круто. Я трохи змінив його, щоб використовувати масив замість POJO, і я просто використовую індекс для дужки масиву.
Ніколас Джентіле

1
Це стандартний спосіб обробки комутаторів у Python. У цьому випадку мені це подобається більше завдяки чудовій читабельності.
Я

12
Цей підхід має свої межі та накладні витрати. Кожне з ваших переглядів буде оброблено і буде залежати від поточного стану / реквізиту, якого може не бути. Приклад: скажімо, ви хотіли або відтворити: <SearchResults />або <NoResults />. Якщо стан подання повинен відображатися <NoResults />, <SearchResults />можливо, не компіляція, оскільки це залежить від властивостей, які ще не існують.
ABCD.ca

3
А як щодо випадку за замовчуванням?
lama12345

4
@ lama12345 Для випадку за замовчуванням використовуйте ||наступне:{ 'foo': <Foo />, 'bar': <Bar /> }[param] || <Baz />
Аарон Адріан,

54

Це відбувається, оскільки switchтвердження є statement, але тут javascript очікує виразу.

Хоча, не рекомендується використовувати оператор switch у renderметоді, для досягнення цього ви можете використовувати функцію самовиклику:

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 Замість функції 'const project' усередині методу візуалізації ми можемо розмістити її як метод компонента, а потім викликати всередині повернення рендера, як "<div> {this.project ()} </div>". Якщо ви не говорите про те, щоб взагалі не використовувати перемикач, я можу подумати про використання if / else або показати / приховати компоненти за допомогою className, оновивши стан.
Вільямсі

це може бути навіть більше, бо, наприклад, я використовую 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 повинна бути правильною, оскільки перемикач не повинен використовуватися у функціональному компоненті. Дякуємо за додавання АБО для випадку за замовчуванням. І не турбуйтеся про rswitch (), рішення на карті вже на місці! великі пальці вгору
Eugenijus S.

15

Спосіб представити своєрідний комутатор у блоці візуалізації, використовуючи умовні оператори:

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

Слід забезпечити, щоб умови суворо повертали логічне значення.


2
Гарний та елегантний, його можна використовувати прямо в блоці візуалізації. Найкраща відповідь IMHO
GavinBelson

2
Я помітив, що це порушення правил EsLints eslint.org/docs/rules/no-mixed-operators mix && та ||
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 Оновлено трохи докладніше.
Matt Way

@MattWay Хоча це хороший варіант для чистого JS, він видає помилку TS, коли ціль встановлена ​​на es5, оскільки пошук властивості не існує в ReactNode
Zakriya Bilal

@ZakriyaBilal Код - це більше доказ концепції, а також про те, як такі компоненти, як комутатори, змусять ваш код більше реагувати. Головне - просто знайти спосіб порівняти реквізит дітей.
Matt 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

Ви не можете мати перемикач у візуалізації. Підхід psuedo-switch до розміщення об’єкта-літералу, який отримує доступ до одного елемента, не є ідеальним, оскільки спричиняє обробку всіх подань, і це може призвести до помилок залежності реквізитів, які не існують у такому стані.

Ось приємний чистий спосіб зробити це, який не вимагає кожного подання заздалегідь:

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.