Search code examples
reactjsreact-nativeconditional-operatorconditional-rendering

How do I style a react component based on multiple conditions?


I have a List of products with their expiry dates. I want to apply styles depending on whether my item is going to expire today, not expire today or has already expired.(There could be many more conditions which I want to work with).
I have successfully managed to do it using nested ternary operator or using an array of styles with ternary operator as shown below.

 <ListItem  
     containerStyle={
          [exp_check === -1 ? { backgroundColor: '#ff9ea5' } : null,
                exp_check === 0 ? { backgroundColor: '#fff185' } : null]
                    }
     badge={
          exp_check !== 1 ?
               exp_check === -1 ? { status: 'error', value: `!` } : { status: 'warning'} : null
           }
/>

Is there a way to achieve something like a switch statement for the style or for any other prop for that matter. I want to be able to easily set my props conditionally without having to write nested logic or arrays. Something along the lines of :

stlye / badge / any prop accepted by the component = {
switch(something):
CASE1: ..
CASE2:.. 
etc etc 
CASE N:
}

I am not sure if I can write an IF/ELSE statement inside the prop because I have not been able to get it to compile if I try to do that.


Solution

  • Consider an approach where you have a categorization function that bins a given item into a particular group, then map props or styles or classnames onto the groups.

    const ONE_HOUR = 1000 * 60 * 60;
    const ONE_DAY = ONE_HOUR * 24;
    
    // an array of status names/labels, each with a predicate function
    // to test whether a given item matches. first match wins.
    const bins = [
      {
        status: 'expired',
        predicate: time => time < Date.now(),
      },
      {
        status: 'urgent',
        predicate: time => Date.now() - time < ONE_HOUR
      },
      {
        status: 'soon',
        predicate: time => Date.now() - time < ONE_DAY,
      },
      {
        status: 'normal'
        predicate: () => true
      }
    }
    
    // find the first bin whose predicate function returns true for the item and use that bin's 'status'
    const expirationStatus = bins.find(bin => bin.predicate(item.expirationTime)).status;
    
    // now expirationStatus is one of 'expired', 'urgent', 'soon', or 'normal'
    // which you can then use to assign styles or classNames or whatever:
    
    // these could live in the bins too, as a 'style' or 'className' property or whatever.
    const styles = {
      expired: {
        background: 'grey',
      },
      urgent: {
        background: 'red'
      },
      soon: {
        background: 'yellow'
      },
      normal: {
        background: 'green'
      }
    }
    
    return (
      <Component
        style={styles[expirationStatus]} {/* this */}
        status={expirationStatus} {/* and/or this */}
        className={expirationStatus} {/* and/or this */}
      />
    )