0

I am trying to apply a Ternary operator to some JSON Data which is held in a separate file locally. Below is the JSON:

[
{
  "id": 1,
  "company": "Photosnap",
  "logo": "./images/photosnap.svg",
  "new": true,
  "featured": true,
  "position": "Senior Frontend Developer",
  "role": "Frontend",
  "level": "Senior",
  "postedAt": "1d ago",
  "contract": "Full Time",
  "location": "USA Only",
  "languages": ["HTML", "CSS", "JavaScript"]
},
{
  "id": 2,
  "company": "Manage",
  "logo": "./images/manage.svg",
  "new": true,
  "featured": true,
  "position": "Fullstack Developer",
  "role": "Fullstack",
  "level": "Midweight",
  "postedAt": "1d ago",
  "contract": "Part Time",
  "location": "Remote",
  "languages": ["Python"],
  "tools": ["React"]
},
{
  "id": 3,
  "company": "Account",
  "logo": "./images/account.svg",
  "new": true,
  "featured": false,
  "position": "Junior Frontend Developer",
  "role": "Frontend",
  "level": "Junior",
  "postedAt": "2d ago",
  "contract": "Part Time",
  "location": "USA Only",
  "languages": ["JavaScript"],
  "tools": ["React"

Now the issue I have is I conditionally want to show a button dependent on whether "new" is true. See Example The same is said to be with the Featured button. So I have written a Ternary Operator in my Component.

import React from 'react';

import './job-card.styles.css';

const JobCard = ({company, position, postedAt, contract, location, logo, featured, newJob })    => (

<div className="container">
   <div className='card'>
     <div className='companyName'>
        <img src={logo} alt="logo" width="100" height="100"></img>
    </div>
    <div className='content'>

        {{newJob} ? <button className='myButton'>New!</button> : null }
        {{featured} ? <button className='myDarkButton'>Featured</button> : null }
        <h2>{company}</h2>
        <h1>{position}</h1>
        <div className='details'>
            <h3>{postedAt} &#183;</h3>
            <h3>{contract} &#183;</h3>
            <h3>{location}</h3>
        </div>

      </div>
  </div>
 </div>
 )

 export default JobCard;

This is just a card component and feeds into another component which displays all the cards.

import React from 'react';

import './job-listing.styles.css';
import JobCard from '../job-card/job-card.component.jsx/job-card.component';
import { Component } from 'react';


class JobListing extends Component {
constructor() {
    super();
    this.state = {
        jobs: []
    }
  };

 componentDidMount() {
    fetch('/data.json')
    .then(response => response.json())
    .then(data => this.setState({jobs: data}))

   }
  render() {
    return (
        <div>
            {this.state.jobs.map(({id, ...otherJobProps}) =>(
            <JobCard key={id} {...otherJobProps} />
            ))}
        </div>
       )
     }
   }

  export default JobListing;

The output I am getting is that they are all rendering as true when some of the new or featured are false in the JSON Data. Not sure what I have missed. Any help would be appreciated.

AltBrian
  • 2,392
  • 9
  • 29
  • 58
  • `{newJob}` is an object, not a value. Even if `newJob` is `undefined`, you would have an object like `{newJob: undefined}` that evaluates as `true`, so renders the component – Rashomon May 18 '20 at 14:16

3 Answers3

1

The problem is the inner {}.

{{newJob} ? <button className='myButton'>New!</button> : null }
// ^ here

Within JSX, {} denotes a javascript expression. But once you are within an expression, {} goes back to being normal object syntax. This is throwing off your ternary because you're checking whether an object with key newJob is truthy. Simply removing the brackets would fix it:

{newJob ? <button className='myButton'>New!</button> : null }

Regarding the new issue

I prefer not to destructure props like this, but to get it working most like you already have, destructure the new reserved word into an alias. Here is a simple proof of concept:

let test = [{ new: true }, { new: false }];

test.map(({new: isNew}) => console.log(isNew))

I would prefer to keep the data structured as is. But thats just a preference. It would also avoid the reserved word issue.

let test = [{ new: true }, { new: false }];

test.map((value) => console.log(value.new))
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
  • Now it worked for featured but not for newJob as the property in JSON is new. So I changed to new and now I get the following error ```Parsing error: Unexpected keyword 'new'``` – AltBrian May 18 '20 at 14:21
  • `new` is reserved, so you'll have to alias it when destructuring your props. I'll add it to the answer. – Brian Thompson May 18 '20 at 14:22
  • Checkout [this answer](https://stackoverflow.com/questions/38762715/how-to-destructure-object-properties-with-key-names-that-are-invalid-variable-na) for the `new` issue: – Brian Thompson May 18 '20 at 14:29
  • Basically you'll want to change your `map` to something like this: `map(({id, new: newJob,...otherJobProps})` – Brian Thompson May 18 '20 at 14:31
  • Thanks for your help Brian. Here is what I have done ``` { {new : newJob} ? : null }``` However this is not working for me. – AltBrian May 18 '20 at 15:00
  • No thats not what I meant. The change I suggested was for the `map` function inside `JobListing`, not for the ternary. The ternary will remain `{newJob ? //buton : null}`. Only change the `map` – Brian Thompson May 18 '20 at 15:02
  • So I have done this in the job-card component ```{ newJob ? : null }``` and this in the job-listing component ```{this.state.jobs.map(({id, new: newJob,...otherJobProps}) =>( ))}``` – AltBrian May 18 '20 at 15:06
  • Close. Just remember to also add `newJob` as a prop to `JobCard`. You could also do it in the definition of `JobCard`. As long as you rename the reserved word. But as you can see, this is why I prefer to just not destructure these types of things – Brian Thompson May 18 '20 at 15:07
  • The button does not appear. ```const JobCard = ({company, position, postedAt, contract, location, logo, featured, newJob }) => (
    logo
    { newJob ? : null } {featured ? : null } ```
    – AltBrian May 18 '20 at 15:10
  • 1
    Try this instead.. `const JobCard = ({company, position, postedAt, contract, location, logo, featured, new: newJob }) => (` – Brian Thompson May 18 '20 at 15:11
  • Thank you! Life saver – AltBrian May 18 '20 at 15:14
0

In your case, you can simply do:

{newJob 
    && (<button className='myButton'>New!</button>)
}
{featured 
    && <button className='myDarkButton'>Featured</button>
}

It works because in JavaScript, true && expression always evaluates to expression,

and false && expression always evaluates to false.

Therefore, if the condition is true, the element right after && will appear in the output. If it is false, React will ignore and skip it.

And null, undefined, 0, "" are falsy values in JS.


Ternary operator is also an option if you really need two options (e.g):

<div>
    The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
ikos23
  • 4,879
  • 10
  • 41
  • 60
0

First of all, in the json objects I see the property new: true but the JobCard component receives newJob as part of it's props, not new.

To answer your question you see the buttons rendered because the condition {newJob} is an object which evaluates to true in:

{{newJob} ? <button className='myButton'>New!</button> : null }

Why? Because {newJob} is the same as this: { newJob: newJob } which is creating an object with a property called newJob and assigning it the value of newJob that you get from the component props.

What you want to do is one of the following:

{newJob ? <button className='myButton'>New!</button> : null }
{newJob && <button className='myButton'>New!</button> }
alewis729
  • 249
  • 3
  • 8