จะใช้คำสั่ง switch ภายในส่วนประกอบ React ได้อย่างไร?


120

ฉันมีองค์ประกอบการตอบสนองและในrenderวิธีการของส่วนประกอบฉันมีสิ่งนี้:

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

           { switch(...) {} }

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

ตอนนี้ประเด็นคือฉันมีสองdivองค์ประกอบหนึ่งที่ด้านบนและอีกหนึ่งที่ด้านล่างซึ่งได้รับการแก้ไขแล้ว ตรงกลางฉันต้องการมีคำสั่งสวิตช์และตามค่าในสถานะของฉันฉันต้องการแสดงองค์ประกอบอื่น โดยพื้นฐานแล้วฉันต้องการให้ทั้งสองdivองค์ประกอบได้รับการแก้ไขเสมอและตรงกลางเพื่อแสดงองค์ประกอบที่แตกต่างกันในแต่ละครั้ง ฉันใช้สิ่งนี้เพื่อใช้ขั้นตอนการชำระเงินหลายขั้นตอน) แม้ว่าตอนนี้รหัสจะใช้งานไม่ได้เนื่องจากมีข้อผิดพลาดแจ้งว่าswitchไม่คาดคิด มีความคิดอย่างไรที่จะบรรลุสิ่งที่ฉันต้องการ?


1
คุณไม่จำเป็นต้องมีตรรกะทั้งหมดนั้นในreturnคำสั่งหรือแม้แต่renderวิธีการสำหรับเรื่องนั้น คุณสามารถกำหนดแต่ละ<div>เป็น const และจากนั้นใช้switch ก่อนของคุณreturnเพื่อตรวจสอบว่า<div>ควรจะแสดงผล?
Jared Goguen

@JaredGoguen แต่จากนั้นฉันจะต้องทำซ้ำdivที่ด้านบนและด้านล่างหลาย ๆ ครั้งสำหรับแต่ละกรณีของไฟล์switch. หรือผมเข้าใจผิดคุณ ..
พิมพ์ผิด

ไม่คุณสามารถสร้างโค้ดสำหรับlet middleDiv = ...และรวม{middleDiv}ไว้ใน JSX ที่ส่งคืนระหว่างสองค่า<div>ที่คุณได้กำหนดไว้ที่นั่น
Jared Goguen

คำตอบ:


218

ลองวิธีนี้ซึ่งเป็นวิธีที่สะอาดกว่าด้วย: ถอดสวิตช์นั้นออกจากการเรนเดอร์ในฟังก์ชันและเรียกมันว่าส่งผ่านพารามิเตอร์ที่คุณต้องการ ตัวอย่างเช่น:

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


103

ในทางตรงกันข้ามกับคำตอบอื่น ๆ ฉันต้องการที่จะแทรก "สวิตช์" ในฟังก์ชันการแสดงผล ทำให้ชัดเจนยิ่งขึ้นว่าส่วนประกอบใดบ้างที่สามารถแสดงผลได้ที่ตำแหน่งนั้น คุณสามารถใช้นิพจน์เหมือนสวิตช์ได้โดยใช้วัตถุจาวาสคริปต์แบบเก่า:

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

1
มันเจ๋งมาก ฉันแก้ไขเล็กน้อยเพื่อใช้อาร์เรย์แทน POJO และฉันแค่ใช้ดัชนีเพื่อยึดเข้ากับอาร์เรย์
Nicholas Gentile

1
นี่เป็นวิธีมาตรฐานในการจัดการกับสวิตช์เคสใน Python ฉันชอบมันมากกว่าในอินสแตนซ์นี้เนื่องจากความสามารถในการอ่านที่เหนือกว่า
ฉันจะกินหมวกของฉัน

16
แนวทางนี้มีขีด จำกัด และค่าใช้จ่าย แต่ละมุมมองของคุณจะได้รับการประมวลผลและจะขึ้นอยู่กับสถานะ / อุปกรณ์ประกอบฉากปัจจุบันซึ่งอาจไม่มีอยู่จริง Ex: ช่วยบอกว่าคุณต้องการที่จะแสดงผลอย่างใดอย่างหนึ่ง: หรือ<SearchResults /> <NoResults />ถ้าดูสถานะควรทำให้<NoResults />, <SearchResults />อาจจะไม่รวบรวมเพราะมันขึ้นอยู่กับคุณสมบัติที่ยังไม่อยู่
ABCD.ca

6
แล้วกรณีเริ่มต้นล่ะ?
kungfooman

7
@ lama12345 สำหรับกรณีเริ่มต้นให้ใช้||ดังนี้{ 'foo': <Foo />, 'bar': <Bar /> }[param] || <Baz />
Aaron Adrian

56

ที่เกิดขึ้นเพราะswitchคำสั่งคือstatementแต่ที่นี่จาวาสคริปต์คาดว่าจะมีการแสดงออก

แม้ว่าจะไม่แนะนำให้ใช้คำสั่ง 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

ทำไมถึงไม่แนะนำ?
bluenote10

เขาคงอ่านอะไรบางอย่างที่บอกว่าอย่าทำเพราะมันน่าเกลียดสำหรับใครบางคน หลายคนไม่ชอบเปลี่ยนคำสั่งด้วยเหตุผลที่แตกต่างกัน แม้ว่าจะไม่ได้ระบุไว้ในเอกสาร React แต่การแสดงผลตามเงื่อนไขได้รับการสนับสนุนโดย React อย่างชัดเจนและคำสั่งการสลับจะไม่ก่อให้เกิดปัญหาใด ๆ กับ React
วิลเลียม

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 () return สะอาดดังนั้นฉันจึงใส่ตรรกะของฉันไว้ในฟังก์ชัน 'const' ด้านบน วิธีนี้ฉันยังสามารถเยื้องเคสสวิตช์ของฉันได้อย่างเรียบร้อย


@a_m_dev แทนที่จะเป็นฟังก์ชัน "const project" ในเมธอดการเรนเดอร์เราสามารถวางเป็นเมธอดคอมโพเนนต์จากนั้นเรียกมันภายใน render return เช่น "<div> {this.project ()} </div>" เว้นแต่คุณจะพูดถึงการไม่ใช้สวิตช์เลยฉันสามารถคิดว่าจะใช้ if / else หรือแสดง / ซ่อนส่วนประกอบโดยใช้ className โดยการอัปเดตสถานะ
williamsi

นั่นอาจจะดีกว่านี้เพราะตัวอย่างเช่นฉันใช้head()วิธีการของส่วนประกอบเส้นทางของฉันเพื่อฉีดข้อมูลreact-helmetไปที่ส่วนหัวของเอกสารของฉัน
a_m_dev

16

วิธีแสดงสวิตช์ชนิดหนึ่งในบล็อกการแสดงผลโดยใช้ตัวดำเนินการตามเงื่อนไข:

{(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ผสม && และ ||
FishFingers

1
@FishFingers ฉันสังเกตเห็นเช่นกันเมื่อฉันพยายามใช้มันตรงตามข้างบน สามารถหลีกเลี่ยงได้ง่ายโดยการใส่ "case" แต่ละตัวไว้ในวงเล็บ
Ch0sen

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] ไม่ใช่วิธีแก้ปัญหาที่ดี คุณจะเห็นก่อนที่การเลือกจะเกิดขึ้นวัตถุเริ่มต้นทั้งหมดจะถูกสร้างขึ้นกล่าวคือทุกสาขาของสวิตช์จะแสดงผล - Component1, Component2 ฯลฯ ...
Gleb Varenov

ใช่คำตอบของ lenkan ควรเป็นคำตอบที่ถูกต้องเพราะไม่ควรใช้สวิตช์ในองค์ประกอบการทำงาน ขอขอบคุณที่เพิ่มหรือสำหรับกรณีเริ่มต้น และอย่ากังวลกับ rswitch () การแก้ปัญหาแผนที่เป็นจุด! ยกนิ้วให้
Eugenijus S.

14

ฉันไม่ใช่แฟนตัวยงของคำตอบใด ๆ ในปัจจุบันเพราะคำตอบเหล่านี้เป็นคำที่ละเอียดเกินไปหรือต้องการให้คุณข้ามรหัสเพื่อทำความเข้าใจว่าเกิดอะไรขึ้น

ฉันชอบทำสิ่งนี้ในลักษณะที่เป็นศูนย์กลางของปฏิกิริยามากกว่าโดยการสร้างไฟล์<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 ในการวาง object-literal ที่เข้าถึงองค์ประกอบหนึ่งไม่เหมาะอย่างยิ่งเนื่องจากทำให้มุมมองทั้งหมดประมวลผลและอาจส่งผลให้เกิดข้อผิดพลาดในการพึ่งพาของอุปกรณ์ประกอบฉากที่ไม่มีอยู่ในสถานะนั้น

นี่เป็นวิธีที่ดีในการดำเนินการที่ไม่ต้องการให้แต่ละมุมมองแสดงผลล่วงหน้า:

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

หากเงื่อนไขของคุณที่สถานะมุมมองขึ้นอยู่กับคุณสมบัติธรรมดา ๆ มากกว่าเช่นหลายเงื่อนไขต่อบรรทัด enum และ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>;

0

ฉันไม่พอใจกับคำตอบใด ๆ แต่ฉันได้รับแนวคิดบางอย่างจาก @Matt Way

นี่คือทางออกของฉัน:

คำจำกัดความ:

const Switch = props => {
    const { test, children = null } = props;
    return children && children.find(child => child && child.props && child.props.casevalue === test) || null;
}

const Case = ({ casevalue = false, children = null }) => <div casevalue={`${casevalue}`}>{children}</div>;

Case.propTypes = {
    casevalue: PropTypes.string.isRequired,
    children: PropTypes.node.isRequired,
}

const Default = ({ children }) => children || <h1>NO_RESULT</h1>;

const SwitchCase = ({ test, cases = [], defaultValue = null }) => {

    const defaultVal = defaultValue
        && React.cloneElement(defaultValue, { key: 'default-key', casevalue: `${test}` })
        || <Default key='default-key' casevalue={`${test}`} />;

    return (
        <Switch test={`${test}`} >
            {
                cases.map((cas, i) => {
                    const { props = {} } = cas || {};
                    const { casevalue = false, ...rest } = props || {};

                    return <Case key={`case-key-${i}`} casevalue={`${casevalue}`}>{ React.cloneElement(cas, rest)}</Case>
                })
                .concat(defaultVal)
            }
        </Switch>
    );
}

การใช้งาน:

<SwitchCase
  cases={[
    <div casevalue={`${false}`}>#1</div>,
    <div casevalue={`${true}`}>#2</div>,
    <div casevalue={`${false}`}>#3</div>,
  ]}
  defaultValue={<h1>...nothing to see here</h1>} // You can leave it blank.
  test={`${true}`}
/>

0

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.