简体   繁体   中英

Do not nest ternary expression - alternative

I have this React component that can has 4 states: advisoryResults, results, noResults, newAccount.

I think it makes sense to do it with a ternary expression but this is not allowed, what can be a good alternative for that?

export default class Home extends React.Component {
  state = {
    isAdmin: false
  };

  render() {
    const { isAdmin } = this.state;
    return (
      <div>
        <Header />
        <div css={innerWrap}>
          {isAdmin ? (
            <Button
              onClick={this.handleAddNewContact }
            >
              Add new Contact
            </Button>
          ) : (
            ''
          )}
   {searchMode === searchModes.advisoryPanels ? (
            <>
              <SearchAdvisoryPanels />
              <div css={{ textAlign: 'center', margin: '60px auto' }}>
                <ManAtDesk />
              </div>
            </>
          ) : searchMode === searchModes.noResultsPanel ? (
            <SearchNoResultsPanel />
          ) : searchMode === searchModes.resultsPanel ? (
            accountInfo.map((info, index) => (
              <SearchResultPanel info={info} isAdmin={isAdmin} key={index} />
            ))
          ) : searchMode === searchModes.addContactPanel ? (
            <AddNewContactForm
              onCancelAccount={this.onCancelAccount}
              onSaveAccount={this.onSaveAccount}
            />
          ) : null}
        </div>
      </div>
    );
  }
}

Please advise.

I would suggest doing something like this:

export default class Home extends React.Component {
  state = {
    isAdmin: false
  };

  onCancelAccount = () => {};
  onSaveAcciont = () => {};

  renderSearchResults = (searchMode) => {
    const { info, isAdmin, index } = this.props;

    switch (searchMode) {
      case searchModes.advisoryPanels:
        return this.renderAdvisoryPanels();
      case searchModes.noResultsPanel:
        return <SearchNoResultsPanel />;
      case searchModes.resultsPanel:
        return <SearchResultPanel info={info} isAdmin={isAdmin} key={index} />;
      case searchModes.addContactPanel:
        return (
          <AddNewContactForm
            onCancelAccount={this.onCancelAccount}
            onSaveAccount={this.onSaveAccount}
          />
        );
      default:
        return null;
    }
  }

  renderAdvisoryPanels = () => (
    <React.Fragment>
      <SearchAdvisoryPanels />
      <div css={{ textAlign: 'center', margin: '60px auto' }}>
        <ManAtDesk />
      </div>
    </React.Fragment>
  );

  render() {
    const { isAdmin } = this.state;

    return (
      <div>
        <Header />
        <div css={innerWrap}>
          {isAdmin && (
            <Button onClick={this.handleAddNewContact}>Add new Contact</Button>
          )}
          {this.renderSearchResults(searchMode)}
        </div>
      </div>
    );
  }
}

Some of the handlers/props are not immediately visible from your example, so fix if I got something wrong.

I was about to write an answer back when you asked this question with a switch statement.

Using a JSON containing every possible results could solve your problem without adding unnecessary code :

render() {
    const { isAdmin } = this.state;
    return (
        <div>
            <Header />
            <div css={innerWrap}>
                {isAdmin && <Button onClick={this.handleAddNewContact}> Add new Contact</Button>}
                {
                    {
                        [searchModes.SearchAdvisoryPanels]: (
                            <>
                                <SearchAdvisoryPanels />
                                <div css={{ textAlign: 'center', margin: '60px auto' }}>
                                    <ManAtDesk />
                                </div>
                            </>
                        ),
                        [searchModes.noResultsPanel]: <SearchNoResultsPanel />,
                        [searchModes.resultsPanel]: accountInfo.map((info, index) => (<SearchResultPanel info={info} isAdmin={isAdmin} key={index} />)),
                        [searchModes.addContactPanel]: <AddNewContactForm  onCancelAccount={this.onCancelAccount} onSaveAccount={this.onSaveAccount} />
                    }[searchMode]
                }
            </div>
        </div>
    );
}

However this solution will mount all your components even though they are not displayed, using arrow functions instead could increase the performance by only mounting the desired component :

render() {
    const { isAdmin } = this.state;
    return (
        <div>
            <Header />
            <div css={innerWrap}>
                {isAdmin && <Button onClick={this.handleAddNewContact}> Add new Contact</Button>}
                {
                    {
                        [searchModes.SearchAdvisoryPanels]: () => (
                            <>
                                <SearchAdvisoryPanels />
                                <div css={{ textAlign: 'center', margin: '60px auto' }}>
                                    <ManAtDesk />
                                </div>
                            </>
                        ),
                        [searchModes.noResultsPanel]: () => <SearchNoResultsPanel />,
                        [searchModes.resultsPanel]: () => accountInfo.map((info, index) => (<SearchResultPanel info={info} isAdmin={isAdmin} key={index} />)),
                        [searchModes.addContactPanel]: () => <AddNewContactForm  onCancelAccount={this.onCancelAccount} onSaveAccount={this.onSaveAccount} />
                    }[searchMode]()
                }
            </div>
        </div>
    );
}

EDIT

If the variable can have a value different from every given option, you can implement the following solutions.

Either send a function that does not do anything if the value is undefined :

}[searchMode] || (x => x)()

Or store the result outside of your return and call the function if it exists :

render() {
    const { isAdmin } = this.state;
    const searchRender = {
        [searchModes.SearchAdvisoryPanels]: () => (
            <>
                <SearchAdvisoryPanels />
                <div css={{ textAlign: 'center', margin: '60px auto' }}>
                    <ManAtDesk />
                </div>
            </>
        ),
        [searchModes.noResultsPanel]: () => <SearchNoResultsPanel />,
        [searchModes.resultsPanel]: () => accountInfo.map((info, index) => (<SearchResultPanel info={info} isAdmin={isAdmin} key={index} />)),
        [searchModes.addContactPanel]: () => <AddNewContactForm onCancelAccount={this.onCancelAccount} onSaveAccount={this.onSaveAccount} />
    }[searchMode]

    return (
        <div>
            <Header />
            <div css={innerWrap}>
                {isAdmin && <Button onClick={this.handleAddNewContact}> Add new Contact</Button>}
                {searchRender && searchRender()}
            </div>
        </div>
    );
}

EDIT : What is this dark magic ?

The first step creates your JSON with computed properties , it allows you to assign an Objects keys from variables rather that hardcoded values :

 const searchModes = { SearchAdvisoryPanels: "a", noResultsPanel: "b", resultsPanel: "c", addContactPanel: "d" } const result = { [searchModes.SearchAdvisoryPanels]: 1, [searchModes.noResultsPanel]: 2 , [searchModes.resultsPanel]: 3, [searchModes.addContactPanel]: 4 } console.log(result) 

We can now replace the values 1 2 3 4 with whatever we want. In this case, arrow functions. To get the result you want, just add braces at the end of the JSON :

 const searchModes = { SearchAdvisoryPanels: "a", noResultsPanel: "b", resultsPanel: "c", addContactPanel: "d" } const search = "c" const result = { [searchModes.SearchAdvisoryPanels]: 1, [searchModes.noResultsPanel]: 2 , [searchModes.resultsPanel]: 3, [searchModes.addContactPanel]: 4 }[search] console.log(result) 

If the values returned are converted to functions, you only have to add parenthesis at the end of your thing to call it :

 const searchModes = { SearchAdvisoryPanels: "a", noResultsPanel: "b", resultsPanel: "c", addContactPanel: "d" } const search = "c" const result = { [searchModes.SearchAdvisoryPanels]: () => 1, [searchModes.noResultsPanel]: () => 2 , [searchModes.resultsPanel]: () => 3, [searchModes.addContactPanel]: () => 4 }[search]() console.log('Short : ' + result) //Long syntax : const options = { [searchModes.SearchAdvisoryPanels]: () => 1, [searchModes.noResultsPanel]: () => 2 , [searchModes.resultsPanel]: () => 3, [searchModes.addContactPanel]: () => 4 } const action = options[search] const output = action() console.log('Long : ' + output) 

This is what you should aways do on JS, move stuff to a fn. This approach keeps the render fn clear. Your getSearchMode has a clear reading, nothing complex is happening there. You are not setting an unnecessary object, not creating many functions inside your render function or doing any magic - just plain old JS.

export default class Home extends React.Component {
  state = {
    isAdmin: false,
  }

  getSearchMode = (searchMode) => {
    if (searchMode === searchModes.advisoryPanels) {
      return (
        <>
          <SearchAdvisoryPanels />
          <div css={{ textAlign: 'center', margin: '60px auto' }}>
            <ManAtDesk />
          </div>
        </>
      )
    }
    if (searchMode === searchModes.noResultsPanel) {
      return <SearchNoResultsPanel />
    }
    if (searchMode === searchModes.resultsPanel) {
      return accountInfo.map((info, index) => (
        <SearchResultPanel info={info} isAdmin={this.state.isAdmin} key={index} /> // you should not use index as key
      ))
    }
    if (searchMode === searchModes.addContactPanel) {
      return (
        <AddNewContactForm
          onCancelAccount={this.onCancelAccount}
          onSaveAccount={this.onSaveAccount}
        />
      )
    }
    return null
  }

  render() {
    const { isAdmin } = this.state
    return (
      <div>
        <Header />
        <div css={innerWrap}>
          {isAdmin ? <Button onClick={this.handleAddNewContact}>Add new Contact</Button> : ''}
          {this.getSearchMode(searchMode)}
        </div>
      </div>
    )
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM