Làm thế nào để sử dụng câu lệnh switch bên trong một thành phần React?


109

Tôi có một thành phần React và bên trong renderphương thức của thành phần này, tôi có một cái gì đó như sau:

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

           { switch(...) {} }

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

Bây giờ vấn đề là tôi có hai divyếu tố, một ở trên cùng và một ở dưới cùng, đã được cố định. Ở giữa, tôi muốn có một câu lệnh switch và theo một giá trị ở trạng thái của tôi, tôi muốn hiển thị một thành phần khác. Vì vậy, về cơ bản, tôi muốn hai divphần tử luôn được cố định và chỉ ở giữa để hiển thị một thành phần khác nhau mỗi lần. Tôi đang sử dụng điều này để triển khai quy trình thanh toán nhiều bước). Tuy nhiên, mã hiện tại nó không hoạt động, vì nó cho tôi một lỗi switchkhông mong muốn. Bất kỳ ý tưởng làm thế nào để đạt được những gì tôi muốn?


1
Chà, bạn không cần phải có tất cả logic đó trong returncâu lệnh hoặc thậm chí là renderphương pháp cho vấn đề đó. Bạn có thể định nghĩa mỗi <div>cái là một hằng số , và sau đó sử dụng cái switch trước của bạn returnđể xác định cái nào <div>nên được hiển thị?
Jared Goguen

@JaredGoguen Nhưng sau đó, tôi sẽ cần phải lặp lại divở trên cùng và dưới cùng, nhiều lần cho mỗi trường hợp của switch. Hoặc tôi chỉ hiểu lầm, bạn ..
lỗi chính tả

không, bạn có thể tạo mã let middleDiv = ...và sau đó đưa {middleDiv}vào JSX trả về của mình giữa hai mã <div>mà bạn đã mã hóa cứng ở đó.
Jared Goguen

Câu trả lời:


199

Hãy thử cách này, cách này cũng gọn gàng hơn: Lấy công tắc đó ra khỏi kết xuất trong một hàm và chỉ cần gọi nó chuyển các tham số bạn muốn. Ví dụ:

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

Ngược lại với các câu trả lời khác, tôi muốn nội dòng "công tắc" trong chức năng kết xuất. Nó làm cho nó rõ ràng hơn những thành phần có thể được hiển thị tại vị trí đó. Bạn có thể triển khai một biểu thức giống như công tắc bằng cách sử dụng một đối tượng javascript cũ thuần túy:

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

1
cái này hay đấy. Tôi đã sửa đổi nó một chút để sử dụng một mảng thay vì POJO và tôi chỉ đang sử dụng chỉ mục để ngoặc vào mảng.
Nicholas Gentile,

1
Đây là cách tiêu chuẩn để xử lý switch-case trong Python. Tôi thích nó hơn trong trường hợp này vì khả năng đọc vượt trội của nó.
Tôi sẽ ăn chiếc mũ của tôi vào

12
Cách tiếp cận này có giới hạn và chi phí của nó. Mỗi chế độ xem của bạn sẽ được xử lý và sẽ phụ thuộc vào trạng thái / đạo cụ hiện tại có thể không tồn tại. Ví dụ: giả sử bạn muốn kết xuất: <SearchResults />hoặc <NoResults />. Nếu trạng thái xem nên hiển thị <NoResults />, <SearchResults />có thể không biên dịch vì nó phụ thuộc vào các thuộc tính chưa tồn tại.
ABCD.ca

3
Điều gì về một trường hợp mặc định?
lama12345

4
@ lama12345 Đối với trường hợp mặc định, hãy sử dụng ||như sau:{ 'foo': <Foo />, 'bar': <Bar /> }[param] || <Baz />
Aaron Adrian

54

Điều đó đang xảy ra, bởi vì switchcâu lệnh là một statement, nhưng ở đây javascript yêu cầu một biểu thức.

Mặc dù, không nên sử dụng câu lệnh switch trong một renderphương thức, bạn có thể sử dụng hàm tự gọi để đạt được điều này:

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

Cảm ơn tôi đã sử dụng nó như: render () {return (<div> {(() => {switch (this.state.type) {case commons.HARD_SOFT: return <HardSoft params = {this.state.param} onSubmitHead = {this.onSubmit} />;}}) ()} </div>);
JhonQO

22

Tôi đã làm điều này bên trong phương thức 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>
    )
  }

Tôi đã cố gắng giữ cho trả về render () sạch sẽ, vì vậy tôi đặt logic của mình trong một hàm 'const' ngay bên trên. Bằng cách này, tôi cũng có thể thụt vào các hộp công tắc của mình một cách gọn gàng.


@a_m_dev Thay vì một hàm 'const project' bên trong phương thức kết xuất, chúng ta có thể đặt nó như một phương thức thành phần, sau đó gọi nó bên trong kết xuất trả về như "<div> {this.project ()} </div>". Trừ khi bạn đang nói về việc hoàn toàn không sử dụng switch, thì tôi có thể nghĩ đến việc sử dụng if / else hoặc hiển thị / ẩn các thành phần bằng className bằng cách cập nhật trạng thái.
williamsi

mà có thể được tốt hơn nữa, vì ví dụ tôi sử dụng một head()phương pháp của các thành phần đường của tôi để tiêm dữ liệu bằng cách react-helmetvào phần đầu của tài liệu của tôi
a_m_dev

15

câu trả lời của lenkan là một giải pháp tuyệt vời.

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

Nếu bạn cần một giá trị mặc định, thì bạn thậm chí có thể làm

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

Ngoài ra, nếu điều đó không phù hợp với bạn, thì bạn có thể làm điều gì đó như

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

với

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

2
{{key1: <Component1 />, ...} [key] không phải là giải pháp tốt. Bạn thấy đấy, trước khi lựa chọn xảy ra, toàn bộ đối tượng ban đầu được xây dựng - tức là mỗi chi nhánh của công tắc được render - Component1, Component2, vv ...
Gleb Varenov

Vâng, câu trả lời của lenkan phải là câu trả lời chính xác, vì switch không nên được sử dụng trong thành phần chức năng. Cảm ơn bạn đã thêm OR cho trường hợp mặc định. Và đừng bận tâm với rswitch (), giải pháp bản đồ là ngay lập tức! thích
Eugenijus S.

15

Một cách để biểu diễn một loại công tắc trong khối kết xuất, sử dụng các toán tử có điều kiện:

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

Cần đảm bảo rằng các điều kiện hoàn toàn trả về một boolean.


2
Đẹp và thanh lịch và có thể được sử dụng ngay trong khối kết xuất. Câu trả lời hay nhất IMHO
GavinBelson

2
Tôi nhận thấy điều này là vi phạm các quy tắc của EsLints eslint.org/docs/rules/no-mixed-operators trộn && và ||
FishFingers

1
@FishFingers Tôi cũng nhận thấy điều đó khi tôi cố gắng sử dụng nó chính xác như trên. Có thể dễ dàng tránh nó bằng cách đặt mỗi "trường hợp" trong ngoặc đơn.
Ch0sen

13

Tôi không phải là fan cuồng của bất kỳ câu trả lời hiện tại nào, vì chúng quá dài dòng hoặc yêu cầu bạn phải nhảy xung quanh mã để hiểu điều gì đang xảy ra.

Tôi thích làm điều này theo cách tập trung vào thành phần phản ứng hơn, bằng cách tạo một <Switch/>. Công việc của thành phần này là lấy một phần hỗ trợ và chỉ hiển thị các phần tử con có phần hỗ trợ con khớp với phần tử này. Vì vậy, trong ví dụ dưới đây, tôi đã tạo một chỗ testdựa trên công tắc và so sánh nó với một chỗ valuedựa trên con cái, chỉ hiển thị những cái phù hợp.

Thí dụ:

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>

Bạn có thể thực hiện chuyển đổi đơn giản hoặc phức tạp tùy thích. Đừng quên thực hiện kiểm tra chặt chẽ hơn các trẻ em và các đạo cụ giá trị của chúng.


3
Chính xác những gì tôi đang tìm kiếm! Đây là một chút cải tiến: Theo 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 ! }; cách đó, bạn có thể viết:javascript <Switch test={true}> <Case value={true}> <ItAlwaysFeelsRightToBeTrue /> </Case> <Case value={false}> <FalseAlarm /> </Case> </Switch>
lsmod

Mặc dù đây là một giải pháp tốt, nhưng việc thêm một lời giải thích vào mã sẽ khiến nó thậm chí còn tốt hơn
papigee

@papigee Đã cập nhật chi tiết hơn một chút.
Matt Way

@MattWay Mặc dù đây là một lựa chọn tốt cho JS thuần túy, nhưng nó gây ra lỗi TS khi mục tiêu được đặt thành es5 vì tính năng tìm thấy thuộc tính không tồn tại trên ReactNode
Zakriya Bilal

@ZakriyaBilal Mã là một bằng chứng về khái niệm và về cách các ý tưởng thành phần như công tắc sẽ làm cho mã của bạn phản ứng như thế nào. Mấu chốt chỉ là tìm cách so sánh đạo cụ của bọn trẻ.
Matt Way

3

Làm thế nào về:

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

Bạn có thể làm một cái gì đó như thế này.

 <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

Bạn không thể có công tắc khi kết xuất. Phương pháp psuedo-switch đặt đối tượng-chữ truy cập vào một phần tử không phải là lý tưởng vì nó khiến tất cả các khung nhìn xử lý và điều đó có thể dẫn đến lỗi phụ thuộc của các đạo cụ không tồn tại ở trạng thái đó.

Đây là một cách dễ dàng để làm điều đó mà không yêu cầu mỗi chế độ xem phải hiển thị trước:

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

Nếu các điều kiện của bạn cho trạng thái chế độ xem dựa trên nhiều thuộc tính đơn giản - như nhiều điều kiện trên mỗi dòng, thì một enum và một getViewStatehàm để đóng gói các điều kiện là một cách hay để tách logic điều kiện này và dọn dẹp kết xuất của bạn.


Cách đơn giản và sạch sẽ.
Abhilekh Singh

2

Đây là một ví dụ hoạt động đầy đủ bằng cách sử dụng nút để chuyển đổi giữa các thành phần

bạn có thể đặt một hàm tạo như sau

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

thì bạn có thể kết xuất các thành phần như sau

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

}

Như bạn có thể thấy, tôi đang sử dụng một nút để chuyển đổi giữa các trạng thái.


1

Tôi thực sự thích đề xuất trong https://stackoverflow.com/a/60313570/770134 , vì vậy tôi đã điều chỉnh nó thành Typescript như vậy

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

-1

Đây là một cách tiếp cận khác.

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

renderStep0() { return 'step 0' }
renderStep1() { return 'step 1' }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.